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