/src/palette.c
C | 423 lines | 329 code | 52 blank | 42 comment | 77 complexity | 906932e5faafa5b3675990d2b68ce6f4 MD5 | raw file
- // palette: a program to tranform from GRAY FLOATS to RGB BYTES
- // SYNOPSIS:
- // palette FROM TO PALSPEC [in.gray [out.rgb]]
- //
- // FROM = real value (-inf=min)
- // TO = real value (inf=max)
- // PALSPEC = named palette (HOT, GLOBE, RELIEF, etc)j
- #include <assert.h>
- #include <math.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdint.h>
- #include <string.h>
- #define PALSAMPLES 1024
- //#define PALSAMPLES 256
- // Idea: The function
- //
- // palette : R -> rgb
- //
- // is described as a composition of two steps
- //
- // quantization : R -> {0, ..., PALSAMPLES-1}
- // lookup : {0, ..., PALSAMPLES} -> rgb
- //
- // The first step is an affine function followed by a rounding, and the second
- // step is the evaluation of a look-up table. The proposed implementation is
- // kept simple and the size of the look-up table is fixed.
- #include "fail.c"
- #include "xmalloc.c"
- #include "xfopen.c"
- struct palette {
- uint8_t t[3*PALSAMPLES];
- float m, M;
- };
- static void fprint_palette(FILE *f, struct palette *p)
- {
- fprintf(f, "palette %g %g\n", p->m, p->M);
- for (int i = 0; i < PALSAMPLES; i++)
- fprintf(f, "\tpal[%d] = {%d %d %d}\n",
- i, p->t[3*i+0], p->t[3*i+1], p->t[3*i+2]);
- }
- static void fill_palette_gray(uint8_t *t)
- {
- float factor = 255.0 / PALSAMPLES;
- for (int i = 0; i < PALSAMPLES; i++)
- for (int j = 0; j < 3; j++)
- t[3*i+j] = factor * i;
- }
- static void fill_palette_hot(uint8_t *t)
- {
- float factor = 255.0 / PALSAMPLES;
- for (int i = 0; i < PALSAMPLES; i++)
- {
- t[3*i+0] = 255;
- if (i < 85)
- t[3*i+0] = 3 * factor * i;
- t[3*i+1] = factor * i;
- t[3*i+2] = factor * i;
- }
- }
- static void fill_palette_with_nodes(struct palette *p, float *n, int nn)
- {
- if (nn < 2) fail("at least 2 nodes required");
- float factor = (PALSAMPLES-1.0)/(nn-1.0);
- //fprintf(stderr, "1pfnnm1 = %g\n", 1 + factor*(nn-1));
- assert(PALSAMPLES == round(1+factor*(nn-1)));
- for (int i = 0; i < nn-1; i++)
- {
- float posi = n[4*i];
- float posnext = n[4*(i+1)];
- //fprintf(stderr, "fwpn node %d : %g %g\n", i, posi, posnext);
- if (posi > posnext)
- fail("palette nodes must be in increasing order");
- int idxi = factor * i;
- int idxnext = factor * (i+1);
- assert(idxi <= idxnext);
- assert(idxnext < PALSAMPLES);
- for (int j = 0; j <= (idxnext-idxi); j++)
- {
- float a = j*1.0/(idxnext - idxi);
- p->t[3*(idxi+j)+0] = (1-a)*n[4*i+1] + a*n[4*(i+1)+1];
- p->t[3*(idxi+j)+1] = (1-a)*n[4*i+2] + a*n[4*(i+1)+2];
- p->t[3*(idxi+j)+2] = (1-a)*n[4*i+3] + a*n[4*(i+1)+3];
- }
- }
- p->m = n[0];
- p->M = n[4*(nn-1)];
- // dirty hack: copy the penultimate position of the palette to the last
- for (int j = 0; j < 3; j++)
- p->t[3*(PALSAMPLES-1)+j] = p->t[3*(PALSAMPLES-2)+j];
- }
- static void set_node_positions_linearly(float *n, int nn, float m, float M)
- {
- float beta = (M - m)/(nn - 1);
- for (int i = 0; i < nn; i++)
- n[4*i] = m + beta * i;
- }
- static float nodes_cocoterrain[] = {
- 1, 0, 0, 0, // black
- 2, 255, 0, 255, // magenta
- 3, 0, 0, 255, // blue
- 4, 0, 255, 255, // cyan
- 5, 0, 255, 0, // green
- 6, 170, 84, 0, // brown
- 7, 255, 255, 255, // white
- };
- static float nodes_dem[] = {
- 0 , 0 , 96.9994 , 70.9997 ,
- 2.6010 , 16.0012 ,121.9997 , 46.9990 ,
- 26.0100 , 231.9990 , 215.0007 , 125.0010,
- 62.4495 , 160.9993 , 67.0012 , 0,
- 88.4595 , 158.0006 , 0 , 0,
- 145.7070, 109.9993, 109.9993, 109.9993,
- 208.1565, 255.0000, 255.0000, 255.0000,
- 255.0000, 255.0000, 255.0000, 255.0000,
- };
- static float nodes_nice[] = {
- 1, 0, 0, 255, // blue
- 2, 255, 255, 255, // white
- 3, 255, 0, 0, // red
- };
- static float nodes_nnice[] = {
- 1, 255, 0, 0, // red
- 2, 0, 0, 0, // white
- 3, 0, 255, 0, // blue
- };
- static float *get_gpl_nodes(char *filename, int *n)
- {
- int bufsize = 0xff, nnodes = 0;
- char buf[bufsize];
- FILE *f = xfopen(filename, "r");
- static float nodes[4*PALSAMPLES];
- while(fgets(buf, bufsize, f) && nnodes < PALSAMPLES) {
- //fprintf(stderr, "s = \"%s\"\n", buf);
- float *t = nodes + 4*nnodes;
- *t = nnodes;
- if (3 == sscanf(buf, "%g %g %g", t+1, t+2, t+3)) {
- //fprintf(stderr, "\tnod[%d] = %g %g %g\n", nnodes, t[1], t[2], t[3]);
- nnodes += 1;
- }
- }
- xfclose(f);
- *n = nnodes;
- return nodes;
- }
- static float *get_gpf_nodes(char *filename, int *n)
- {
- int bufsize = 0xff, nnodes = 0;
- char buf[bufsize];
- FILE *f = xfopen(filename, "r");
- static float nodes[4*PALSAMPLES];
- while(fgets(buf, bufsize, f) && nnodes < PALSAMPLES) {
- //fprintf(stderr, "s = \"%s\"\n", buf);
- float *t = nodes + 4*nnodes;
- *t = nnodes;
- if (4 == sscanf(buf, "%g %g %g %g", t, t+1, t+2, t+3)) {
- t[1] = round(t[1] * 255);
- t[2] = round(t[2] * 255);
- t[3] = round(t[3] * 255);
- //fprintf(stderr, "\tnod[%d]{%g} = %g %g %g\n", nnodes, t[0], t[1], t[2], t[3]);
- nnodes += 1;
- }
- }
- xfclose(f);
- *n = nnodes;
- return nodes;
- }
- static bool hassuffix(const char *s, const char *suf)
- {
- int len_s = strlen(s);
- int len_suf = strlen(suf);
- if (len_s < len_suf)
- return false;
- return 0 == strcmp(suf, s + (len_s - len_suf));
- }
- static void fill_palette(struct palette *p, char *s, float m, float M)
- {
- p->m = m;
- p->M = M;
- if (0 == strcmp(s, "gray"))
- fill_palette_gray(p->t);
- else if (0 == strcmp(s, "hot"))
- fill_palette_hot(p->t);
- else if (0 == strcmp(s, "cocoterrain")) {
- set_node_positions_linearly(nodes_cocoterrain, 7, m, M);
- fill_palette_with_nodes(p, nodes_cocoterrain, 7);
- } else if (0 == strcmp(s, "dem")) {
- set_node_positions_linearly(nodes_dem, 8, m, M);
- fill_palette_with_nodes(p, nodes_dem, 8);
- } else if (0 == strcmp(s, "nice")) {
- set_node_positions_linearly(nodes_nice, 3, m, M);
- fill_palette_with_nodes(p, nodes_nice, 3);
- } else if (0 == strcmp(s, "nnice")) {
- set_node_positions_linearly(nodes_nnice, 3, m, M);
- fill_palette_with_nodes(p, nodes_nnice, 3);
- } else if (hassuffix(s, ".gpl")) {
- int nnodes;
- float *nodes = get_gpl_nodes(s, &nnodes);
- set_node_positions_linearly(nodes, nnodes, m, M);
- fill_palette_with_nodes(p, nodes, nnodes);
- } else if (hassuffix(s, ".gpf")) {
- int nnodes;
- float *nodes = get_gpf_nodes(s, &nnodes);
- set_node_positions_linearly(nodes, nnodes, m, M);
- fill_palette_with_nodes(p, nodes, nnodes);
- }
- else fail("unrecognized palette \"%s\"", s);
- }
- static void get_palette_color(uint8_t *rgb, struct palette *p, float x)
- {
- if (isnan(x)) {
- rgb[0] = rgb[1] = rgb[2] = 0;
- return;
- }
- if (!isfinite(x)) {
- rgb[0] = rgb[1] = rgb[2] = 255;
- return;
- }
- int ix = round((PALSAMPLES-1)*(x - p->m)/(p->M - p->m));
- if (ix < 0) ix = 0;
- if (ix >= PALSAMPLES) ix = PALSAMPLES - 1;
- rgb[0] = p->t[3*ix+0];
- rgb[1] = p->t[3*ix+1];
- rgb[2] = p->t[3*ix+2];
- }
- #include "smapa.h"
- SMART_PARAMETER_SILENT(PALMAXEPS,0)
- static void get_min_max(float *min, float *max, float *x, int n)
- {
- float m = INFINITY, M = -m;
- for (int i = 0; i < n; i++)
- if (isfinite(x[i]))
- {
- m = fmin(m, x[i]);
- M = fmax(M, x[i]);
- }
- if (min) *min = m;
- if (max) *max = M+PALMAXEPS();
- }
- void apply_palette(uint8_t *y, float *x, int n, char *s, float *m, float *M)
- {
- if (!isfinite(*m)) get_min_max(m, 0, x, n);
- if (!isfinite(*M)) get_min_max(0, M, x, n);
- struct palette p[1];
- fill_palette(p, s, *m, *M);
- //fprint_palette(stderr, p);
- for (int i = 0; i < n; i++)
- get_palette_color(y + 3*i, p, x[i]);
- }
- #define PALETTE_MAIN
- #ifdef PALETTE_MAIN
- #include "iio.h"
- #include "xmalloc.c"
- #include "pickopt.c"
- #define OMIT_MAIN_FONTU
- #include "fontu.c"
- #include "fonts/xfonts_all.c"
- SMART_PARAMETER_SILENT(PLEGEND_WIDTH,64)
- SMART_PARAMETER_SILENT(PLEGEND_HEIGHT,256)
- SMART_PARAMETER_SILENT(PLEGEND_MARGIN_LEFT,12)
- SMART_PARAMETER_SILENT(PLEGEND_MARGIN_RIGHT,36)
- SMART_PARAMETER_SILENT(PLEGEND_MARGIN_TOP,12)
- SMART_PARAMETER_SILENT(PLEGEND_MARGIN_BOTTOM,12)
- SMART_PARAMETER_SILENT(PLEGEND_TICKWIDTH,3)
- SMART_PARAMETER_SILENT(PLEGEND_TEXT_XOFFSET,4)
- SMART_PARAMETER_SILENT(PLEGEND_TEXT_YOFFSET,0)
- SMART_PARAMETER_SILENT(PLEGEND_NTICKS,3)
- void save_legend(char *filename_legend, char *palette_id, float m, float M)
- {
- // palette and font structs
- struct bitmap_font f[1] = {reformat_font(*xfont_7x14B, UNPACKED)};
- struct palette p[1]; fill_palette(p, palette_id, m, M);
- // sizes, margins and positions
- int w = PLEGEND_WIDTH();
- int h = PLEGEND_HEIGHT();
- int m_l = PLEGEND_MARGIN_LEFT();
- int m_r = PLEGEND_MARGIN_RIGHT();
- int m_t = PLEGEND_MARGIN_TOP();
- int m_b = PLEGEND_MARGIN_BOTTOM();
- int p_i = m_l; // left boundary of colored part;
- int p_j = m_t; // top boundary of colored part
- int q_i = w - m_r; // right boundary of colored part
- int q_j = h - m_b; // bottom boundary of colored part
- // image with the legend
- uint8_t *rgb = malloc(3*w*h);
- // fill background
- for (int i = 0; i < 3*w*h; i++)
- rgb[i] = 255;
- // transformation "x -> alpha * j + beta" from positions to values
- float alpha = (M - m) / (p_j - q_j);
- float beta = m - alpha * q_j;
- // fill legend colors
- for (int j = p_j; j < q_j; j++)
- for (int i = p_i; i < q_i; i++)
- {
- //float x = m + ((M - m) * (j - p_j)) / (q_j - p_j);
- float x = alpha * j + beta;
- if (i == 64) fprintf(stderr, "j=%d x=%g\n", j, x);
- get_palette_color(rgb + 3*(j*w+i), p, x);
- }
- // border (1-pix black border)
- for (int l = 0; l < 3; l++) {
- for (int j = p_j; j < q_j; j++) {
- rgb[3*(j*w+p_i-1)+l] = 0;
- rgb[3*(j*w+q_i+0)+l] = 0;
- }
- for (int i = p_i-1; i < q_i; i++) {
- rgb[3*((p_j-1)*w+i)+l] = 0;
- rgb[3*((q_j+0)*w+i)+l] = 0;
- }
- }
- // ticks and numbers
- float ticks[][2] = { // table with tick values and positions
- {M, p_j-1},
- {m, q_j},
- {(m+M)/2, (p_j+q_j)/2},
- {(3*m+M)/4, (3*p_j+q_j)/4},
- {(m+3*M)/4, (p_j+3*q_j)/4}
- // TODO: put more ticks (?)
- };
- int nticks = PLEGEND_NTICKS();
- if (nticks != 2 && nticks != 3 && nticks != 5)
- nticks = 2;
- for (int k = 0; k < nticks; k++)
- {
- float x = ticks[k][0]; // tick value
- int j = ticks[k][1]; // tick position inside the legend
- // draw tick
- for (int i = q_i; i < q_i+1+PLEGEND_TICKWIDTH(); i++)
- for (int l = 0; l < 3; l++)
- rgb[3*(j*w+i)+l] = 0;
- // draw number associated to this tick
- char buf[0x100];
- uint8_t bg[3] = { 255, 255, 255}, fg[3] = {0, 0, 0};
- snprintf(buf, sizeof buf, "%g", x);
- int pos_i = q_i + PLEGEND_TICKWIDTH() + PLEGEND_TEXT_XOFFSET();
- int pos_j = j - f->height/2 + PLEGEND_TEXT_YOFFSET();
- put_string_in_rgb_image(rgb,w,h, pos_i, pos_j, fg,bg,0,f,buf);
- }
- // save legend into file
- iio_write_image_uint8_vec(filename_legend, rgb, w, h, 3);
- free(rgb);
- }
- int main_palette(int c, char *v[])
- {
- char *filename_legend = pick_option(&c, &v, "l", "");
- if (c != 4 && c != 5 && c != 6 ) {
- fprintf(stderr, "usage:\n\t%s from to pal [in [out]]\n", *v);
- // 0 1 2 3 4 5
- return 1;
- }
- float from = atof(v[1]);
- float to = atof(v[2]);
- char *palette_id = v[3];
- char *filename_in = c > 4 ? v[4] : "-";
- char *filename_out = c > 5 ? v[5] : "-";
- int w, h;
- float *x = iio_read_image_float(filename_in, &w, &h);
- uint8_t *y = xmalloc(3*w*h);
- apply_palette(y, x, w*h, palette_id, &from, &to);
- iio_write_image_uint8_vec(filename_out, y, w, h, 3);
- if (*filename_legend)
- save_legend(filename_legend, palette_id, from, to);
- free(x);
- free(y);
- return 0;
- }
- #ifndef HIDE_ALL_MAINS
- int main(int c, char **v) { return main_palette(c, v); }
- #endif
- #endif//PALETTE_MAIN