star-cmap

Mapping values onto color ramps
git clone git://git.meso-star.fr/star-cmap.git
Log | Files | Refs | README | LICENSE

scmap.c (9118B)


      1 /* Copyright (C) 2020, 2021, 2023 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "scmap.h"
     17 
     18 #include <rsys/cstr.h>
     19 #include <rsys/dynamic_array_double.h>
     20 #include <rsys/logger.h>
     21 #include <rsys/mem_allocator.h>
     22 #include <rsys/ref_count.h>
     23 
     24 #define MSG_INFO_PREFIX "Star-CMap:\x1b[1m\x1b[32minfo\x1b[0m: "
     25 #define MSG_ERROR_PREFIX "Star-CMap:\x1b[1m\x1b[31merror\x1b[0m: "
     26 #define MSG_WARNING_PREFIX "Star-CMap:\x1b[1m\x1b[33mwarning\x1b[0m: "
     27 
     28 struct scmap {
     29   struct darray_double palette; /* List of color in [0, 1]^3 */
     30 
     31   int verbose;
     32   struct logger* logger;
     33   struct logger logger__;
     34   struct mem_allocator* allocator;
     35   ref_T ref;
     36 };
     37 
     38 /*******************************************************************************
     39  * Helper functions
     40  ******************************************************************************/
     41 static void
     42 print_info(const char* msg, void* ctx)
     43 {
     44   (void)ctx;
     45   fprintf(stderr, MSG_INFO_PREFIX"%s", msg);
     46 }
     47 
     48 static void
     49 print_err(const char* msg, void* ctx)
     50 {
     51   (void)ctx;
     52   fprintf(stderr, MSG_ERROR_PREFIX"%s", msg);
     53 }
     54 
     55 static void
     56 print_warn(const char* msg, void* ctx)
     57 {
     58   (void)ctx;
     59   fprintf(stderr, MSG_WARNING_PREFIX"%s", msg);
     60 }
     61 
     62 static res_T
     63 setup_default_logger(struct mem_allocator* allocator, struct logger* logger)
     64 {
     65   res_T res = RES_OK;
     66   ASSERT(logger);
     67   res = logger_init(allocator, logger);
     68   if(res != RES_OK) return res;
     69   logger_set_stream(logger, LOG_OUTPUT, print_info, NULL);
     70   logger_set_stream(logger, LOG_ERROR, print_err, NULL);
     71   logger_set_stream(logger, LOG_WARNING, print_warn, NULL);
     72   return RES_OK;
     73 }
     74 
     75 static INLINE void
     76 log_msg
     77   (const struct scmap* scmap,
     78    const enum log_type stream,
     79    const char* msg,
     80    va_list vargs)
     81 {
     82   ASSERT(scmap && msg);
     83   if(scmap->verbose) {
     84     res_T res; (void)res;
     85     res = logger_vprint(scmap->logger, stream, msg, vargs);
     86     ASSERT(res == RES_OK);
     87   }
     88 }
     89 
     90 static INLINE void
     91 log_err
     92   (const struct scmap* scmap,
     93    const char* msg, ...)
     94 #ifdef COMPILER_GCC
     95   __attribute((format(printf, 2, 3)))
     96 #endif
     97 ;
     98 
     99 static INLINE void
    100 log_warn
    101   (const struct scmap* scmap,
    102    const char* msg, ...)
    103 #ifdef COMPILER_GCC
    104     __attribute((format(printf, 2, 3)))
    105 #endif
    106 ;
    107 
    108 void
    109 log_err(const struct scmap* scmap, const char* msg, ...)
    110 {
    111   va_list vargs_list;
    112   ASSERT(scmap && msg);
    113 
    114   va_start(vargs_list, msg);
    115   log_msg(scmap, LOG_ERROR, msg, vargs_list);
    116   va_end(vargs_list);
    117 }
    118 
    119 void
    120 log_warn(const struct scmap* scmap, const char* msg, ...)
    121 {
    122   va_list vargs_list;
    123   ASSERT(scmap && msg);
    124 
    125   va_start(vargs_list, msg);
    126   log_msg(scmap, LOG_WARNING, msg, vargs_list);
    127 
    128   va_end(vargs_list);
    129 }
    130 
    131 static FINLINE int
    132 check_palette(const struct scmap_palette* palette)
    133 {
    134   return palette && palette->get_color && palette->ncolors;
    135 }
    136 
    137 static INLINE res_T
    138 setup_palette(struct scmap* scmap, const struct scmap_palette* palette)
    139 {
    140   size_t i;
    141   res_T res = RES_OK;
    142   ASSERT(scmap && check_palette(palette));
    143 
    144   res = darray_double_resize(&scmap->palette, palette->ncolors*3);
    145   if(res != RES_OK) {
    146     log_err(scmap, "Could not allocate the palette -- %s.\n",
    147       res_to_cstr(res));
    148     goto error;
    149   }
    150 
    151   FOR_EACH(i, 0, palette->ncolors) {
    152     double color[3];
    153 
    154     palette->get_color(i, color, palette->context);
    155 
    156     if(color[0] < 0 || color[0] > 1
    157     || color[1] < 0 || color[1] > 1
    158     || color[2] < 0 || color[2] > 1) {
    159       log_err(scmap,
    160         "Invalid color {%g, %g, %g}. Each channel must be in [0, 1].\n",
    161         SPLIT3(color));
    162       res = RES_BAD_ARG;
    163       goto error;
    164     }
    165 
    166     darray_double_data_get(&scmap->palette)[i*3+0] = color[0];
    167     darray_double_data_get(&scmap->palette)[i*3+1] = color[1];
    168     darray_double_data_get(&scmap->palette)[i*3+2] = color[2];
    169   }
    170 
    171 exit:
    172   return res;
    173 error:
    174   darray_double_clear(&scmap->palette);
    175   goto exit;
    176 }
    177 
    178 static INLINE void
    179 fetch_color_nearest
    180   (const struct scmap* scmap,
    181    const size_t icol,
    182    const double u,
    183    double color[3])
    184 {
    185   const size_t i = u < 0.5 ? icol*3 : (icol + 1)*3;
    186   ASSERT(scmap && color && u >= 0 && u <1);
    187   ASSERT(i/3 < darray_double_size_get(&scmap->palette)/3);
    188   color[0] = darray_double_cdata_get(&scmap->palette)[i+0];
    189   color[1] = darray_double_cdata_get(&scmap->palette)[i+1];
    190   color[2] = darray_double_cdata_get(&scmap->palette)[i+2];
    191 }
    192 
    193 static INLINE void
    194 fetch_color_linear
    195   (const struct scmap* scmap,
    196    const size_t icol,
    197    const double u,
    198    double color[3])
    199 {
    200   const size_t i = icol*3;
    201   ASSERT(scmap && color && u >= 0 && u <1);
    202   ASSERT(i/3 < darray_double_size_get(&scmap->palette)/3);
    203 
    204   if(u == 0) {
    205     color[0] = darray_double_cdata_get(&scmap->palette)[i+0];
    206     color[1] = darray_double_cdata_get(&scmap->palette)[i+1];
    207     color[2] = darray_double_cdata_get(&scmap->palette)[i+2];
    208   } else {
    209     const size_t j = (icol+1)*3;
    210     const double* col0;
    211     const double* col1;
    212     ASSERT(j/3 < darray_double_size_get(&scmap->palette)/3);
    213 
    214     col0 = darray_double_cdata_get(&scmap->palette) + i;
    215     col1 = darray_double_cdata_get(&scmap->palette) + j;
    216 
    217     color[0] = u * (col1[0] - col0[0]) + col0[0];
    218     color[1] = u * (col1[1] - col0[1]) + col0[1];
    219     color[2] = u * (col1[2] - col0[2]) + col0[2];
    220   }
    221 }
    222 
    223 static void
    224 release_scmap(ref_T* ref)
    225 {
    226   struct scmap* scmap = CONTAINER_OF(ref, struct scmap, ref);
    227   ASSERT(ref);
    228   darray_double_release(&scmap->palette);
    229   if(scmap->logger == &scmap->logger__) logger_release(&scmap->logger__);
    230   MEM_RM(scmap->allocator, scmap);
    231 }
    232 
    233 /*******************************************************************************
    234  * Exported symbols
    235  ******************************************************************************/
    236 res_T
    237 scmap_create
    238   (struct logger* logger, /* NULL <=> use builtin logger */
    239    struct mem_allocator* mem_allocator, /* NULL <=> use default allocator */
    240    const int verbose, /* Verbosity level */
    241    const struct scmap_palette* palette,
    242    struct scmap** out_scmap)
    243 {
    244   struct scmap* scmap = NULL;
    245   struct mem_allocator* allocator = NULL;
    246   res_T res = RES_OK;
    247 
    248   if(!out_scmap || !check_palette(palette)) {
    249     res = RES_BAD_ARG;
    250     goto error;
    251   }
    252 
    253   allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
    254   scmap = MEM_CALLOC(allocator, 1, sizeof(*scmap));
    255   if(!scmap) {
    256     if(verbose) {
    257       #define ERR_STR "Could not allocate the Star-ColorMap.\n"
    258       if(logger) {
    259         logger_print(logger, LOG_ERROR, ERR_STR);
    260       } else {
    261         fprintf(stderr, MSG_ERROR_PREFIX ERR_STR);
    262       }
    263       #undef ERR_STR
    264     }
    265     res = RES_MEM_ERR;
    266     goto error;
    267   }
    268   ref_init(&scmap->ref);
    269   scmap->allocator = allocator;
    270   scmap->verbose = verbose;
    271   darray_double_init(scmap->allocator, &scmap->palette);
    272 
    273   if(logger) {
    274     scmap->logger = logger;
    275   } else {
    276     res = setup_default_logger(scmap->allocator, &scmap->logger__);
    277     if(res != RES_OK) {
    278       if(verbose) {
    279         fprintf(stderr, MSG_ERROR_PREFIX
    280           "%s: could not setup the Star-ColorMap logger -- %s.\n",
    281           FUNC_NAME, res_to_cstr(res));
    282       }
    283       goto error;
    284     }
    285     scmap->logger = &scmap->logger__;
    286   }
    287 
    288   res = setup_palette(scmap, palette);
    289   if(res != RES_OK) goto error;
    290 
    291 exit:
    292   if(out_scmap) *out_scmap = scmap;
    293   return res;
    294 error:
    295   if(scmap) {
    296     SCMAP(ref_put(scmap));
    297     scmap = NULL;
    298   }
    299   goto exit;
    300 }
    301 
    302 res_T
    303 scmap_ref_get(struct scmap* scmap)
    304 {
    305   if(!scmap) return RES_BAD_ARG;
    306   ref_get(&scmap->ref);
    307   return RES_OK;
    308 }
    309 
    310 res_T
    311 scmap_ref_put(struct scmap* scmap)
    312 {
    313   if(!scmap) return RES_BAD_ARG;
    314   ref_put(&scmap->ref, release_scmap);
    315   return RES_OK;
    316 }
    317 
    318 res_T
    319 scmap_fetch_color
    320   (const struct scmap* scmap,
    321    const double value,
    322    const enum scmap_filter filter,
    323    double color[3])
    324 {
    325   size_t ncolors;
    326   size_t icol;
    327   double cell;
    328   double icell;
    329   double u;
    330   res_T res = RES_OK;
    331 
    332   if(!scmap || !color || (unsigned)filter >= SCMAP_FILTERS_COUNT__) {
    333     res = RES_BAD_ARG;
    334     goto error;
    335   }
    336 
    337   if(value < 0 || value > 1) {
    338     log_err(scmap, "%s: the submitted value must be in [0, 1] -- value = %g.\n",
    339       FUNC_NAME, value);
    340     res = RES_BAD_ARG;
    341     goto error;
    342   }
    343 
    344   ncolors = darray_double_size_get(&scmap->palette)/3;
    345   cell = value * (double)(ncolors-1);
    346 
    347   u = modf(cell, &icell);
    348   icol = (size_t)icell;
    349   ASSERT(icol < ncolors);
    350 
    351   switch(filter) {
    352     case SCMAP_FILTER_NEAREST:
    353       fetch_color_nearest(scmap, icol, u, color);
    354       break;
    355     case SCMAP_FILTER_LINEAR:
    356       fetch_color_linear(scmap, icol, u, color);
    357       break;
    358     default: FATAL("Unreachable code.\n"); break;
    359   }
    360 
    361 exit:
    362   return res;
    363 error:
    364   goto exit;
    365 }
    366