star-cmap

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

commit 1103b0fa523e93202dc1c33ecdf0eee01dcdab90
parent 67f82ddbd79a0a9dd94da30a6df911ca11194cac
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 20 Mar 2020 16:18:47 +0100

Begin the implementation

Implement and test the scmap_<create|ref_get|ref_put> functions

Diffstat:
Acmake/CMakeLists.txt | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scmap.c | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scmap.h | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scmap_palettes.c | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_scmap.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 614 insertions(+), 0 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -0,0 +1,87 @@ +# Copyright (C) 2020 |Meso|Star> (contact@meso-star.com) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +cmake_minimum_required(VERSION 2.8) +project(scmap C) +enable_testing() + +set(SCMAP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) +option(NO_TEST "Do not build tests" OFF) + +################################################################################ +# Check dependencies +################################################################################ +find_package(RCMake 0.4 REQUIRED) +find_package(RSys 0.9 REQUIRED) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) +include(rcmake) +include(rcmake_runtime) + +include_directories(${RSys_INCLUDE_DIR}) + +################################################################################ +# Configure and define targets +################################################################################ +set(VERSION_MAJOR 0) +set(VERSION_MINOR 0) +set(VERSION_PATCH 0) +set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) + +set(SCMAP_FILES_SRC scmap.c scmap_palettes.c) +set(SCMAP_FILES_INC ) +set(SCMAP_FILES_INC_API scmap.h) +set(SCMAP_FILES_DOC COPYING README.md) + +# Prepend each file in the `SCMAP_FILES_<SRC|INC>' list by `SCMAP_SOURCE_DIR' +rcmake_prepend_path(SCMAP_FILES_SRC ${SCMAP_SOURCE_DIR}) +rcmake_prepend_path(SCMAP_FILES_INC ${SCMAP_SOURCE_DIR}) +rcmake_prepend_path(SCMAP_FILES_INC_API ${SCMAP_SOURCE_DIR}) +rcmake_prepend_path(SCMAP_FILES_DOC ${PROJECT_SOURCE_DIR}/../) + +add_library(scmap SHARED ${SCMAP_FILES_SRC} ${SCMAP_FILES_INC} ${SCMAP_FILES_INC_API}) +target_link_libraries(scmap RSys) + +set_target_properties(scmap PROPERTIES + DEFINE_SYMBOL SCMAP_SHARED_BUILD + VERSION ${VERSION} + SOVERSION ${VERSION_MAJOR}) + +rcmake_setup_devel(scmap SCMap ${VERSION} star/scmap_version.h) + +################################################################################ +# Add tests +################################################################################ +if(NOT NO_TEST) + function(new_test _name) + add_executable(${_name} ${SCMAP_SOURCE_DIR}/${_name}.c) + target_link_libraries(${_name} scmap RSys) + add_test(${_name} ${_name}) + endfunction() + + new_test(test_scmap) + +endif() + +################################################################################ +# Define output & install directories +################################################################################ +install(TARGETS scmap + ARCHIVE DESTINATION bin + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) +install(FILES ${SCMAP_FILES_INC_API} DESTINATION include/star) +install(FILES ${SCMAP_FILES_DOC} DESTINATION share/doc/star-cmap) + diff --git a/src/scmap.c b/src/scmap.c @@ -0,0 +1,272 @@ +/* Copyright (C) 2020 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "scmap.h" + +#include <rsys/cstr.h> +#include <rsys/dynamic_array_double.h> +#include <rsys/logger.h> +#include <rsys/mem_allocator.h> +#include <rsys/ref_count.h> + +#define MSG_INFO_PREFIX "Star-CMap:\x1b[1m\x1b[32minfo\x1b[0m: " +#define MSG_ERROR_PREFIX "Star-CMap:\x1b[1m\x1b[31merror\x1b[0m: " +#define MSG_WARNING_PREFIX "Star-CMap:\x1b[1m\x1b[33mwarning\x1b[0m: " + +struct scmap { + struct darray_double palette; /* List of color in [0, 1]^3 */ + + int verbose; + struct logger* logger; + struct logger logger__; + struct mem_allocator* allocator; + ref_T ref; +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +print_info(const char* msg, void* ctx) +{ + (void)ctx; + fprintf(stderr, MSG_INFO_PREFIX"%s", msg); +} + +static void +print_err(const char* msg, void* ctx) +{ + (void)ctx; + fprintf(stderr, MSG_ERROR_PREFIX"%s", msg); +} + +static void +print_warn(const char* msg, void* ctx) +{ + (void)ctx; + fprintf(stderr, MSG_WARNING_PREFIX"%s", msg); +} + +static res_T +setup_default_logger(struct mem_allocator* allocator, struct logger* logger) +{ + res_T res = RES_OK; + ASSERT(logger); + res = logger_init(allocator, logger); + if(res != RES_OK) return res; + logger_set_stream(logger, LOG_OUTPUT, print_info, NULL); + logger_set_stream(logger, LOG_ERROR, print_err, NULL); + logger_set_stream(logger, LOG_WARNING, print_warn, NULL); + return RES_OK; +} + +static INLINE void +log_msg + (const struct scmap* scmap, + const enum log_type stream, + const char* msg, + va_list vargs) +{ + ASSERT(scmap && msg); + if(scmap->verbose) { + res_T res; (void)res; + res = logger_vprint(scmap->logger, stream, msg, vargs); + ASSERT(res == RES_OK); + } +} + +static INLINE void +log_err + (const struct scmap* scmap, + const char* msg, ...) +#ifdef COMPILER_GCC + __attribute((format(printf, 2, 3))) +#endif +; + +static INLINE void +log_warn + (const struct scmap* scmap, + const char* msg, ...) +#ifdef COMPILER_GCC + __attribute((format(printf, 2, 3))) +#endif +; + +void +log_err(const struct scmap* scmap, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(scmap && msg); + + va_start(vargs_list, msg); + log_msg(scmap, LOG_ERROR, msg, vargs_list); + va_end(vargs_list); +} + +void +log_warn(const struct scmap* scmap, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(scmap && msg); + + va_start(vargs_list, msg); + log_msg(scmap, LOG_WARNING, msg, vargs_list); + + va_end(vargs_list); +} + +static FINLINE int +check_palette(const struct scmap_palette* palette) +{ + return palette && palette->get_color && palette->ncolors; +} + +static INLINE res_T +setup_palette(struct scmap* scmap, const struct scmap_palette* palette) +{ + size_t i; + res_T res = RES_OK; + ASSERT(scmap && check_palette(palette)); + + res = darray_double_resize(&scmap->palette, palette->ncolors*3); + if(res != RES_OK) { + log_err(scmap, "Could not allocate the palette -- %s.\n", + res_to_cstr(res)); + goto error; + } + + FOR_EACH(i, 0, palette->ncolors) { + double color[3]; + + palette->get_color(i, color, palette->context); + + if(color[0] < 0 || color[0] > 1 + || color[1] < 0 || color[1] > 1 + || color[2] < 0 || color[2] > 1) { + log_err(scmap, + "Invalid color {%g, %g, %g}. Each channel must be in [0, 1].\n", + SPLIT3(color)); + res = RES_BAD_ARG; + goto error; + } + + darray_double_data_get(&scmap->palette)[i*3+0] = color[0]; + darray_double_data_get(&scmap->palette)[i*3+1] = color[1]; + darray_double_data_get(&scmap->palette)[i*3+2] = color[2]; + } + +exit: + return res; +error: + darray_double_clear(&scmap->palette); + goto exit; +} + +static void +release_scmap(ref_T* ref) +{ + struct scmap* scmap = CONTAINER_OF(ref, struct scmap, ref); + ASSERT(ref); + darray_double_release(&scmap->palette); + if(scmap->logger == &scmap->logger__) logger_release(&scmap->logger__); + MEM_RM(scmap->allocator, scmap); +} + +/******************************************************************************* + * Exported symbols + ******************************************************************************/ +res_T +scmap_create + (struct logger* logger, /* NULL <=> use builtin logger */ + struct mem_allocator* mem_allocator, /* NULL <=> use default allocator */ + const int verbose, /* Verbosity level */ + const struct scmap_palette* palette, + struct scmap** out_scmap) +{ + struct scmap* scmap = NULL; + struct mem_allocator* allocator = NULL; + res_T res = RES_OK; + + if(!out_scmap || !check_palette(palette)) { + res = RES_BAD_ARG; + goto error; + } + + allocator = mem_allocator ? mem_allocator : &mem_default_allocator; + scmap = MEM_CALLOC(allocator, 1, sizeof(*scmap)); + if(!scmap) { + if(verbose) { + #define ERR_STR "Could not allocate the Star-ColorMap.\n" + if(logger) { + logger_print(logger, LOG_ERROR, ERR_STR); + } else { + fprintf(stderr, MSG_ERROR_PREFIX ERR_STR); + } + #undef ERR_STR + } + res = RES_MEM_ERR; + goto error; + } + ref_init(&scmap->ref); + scmap->allocator = allocator; + scmap->verbose = verbose; + darray_double_init(scmap->allocator, &scmap->palette); + + if(logger) { + scmap->logger = logger; + } else { + res = setup_default_logger(scmap->allocator, &scmap->logger__); + if(res != RES_OK) { + if(verbose) { + fprintf(stderr, MSG_ERROR_PREFIX + "%s: could not setup the Star-ColorMap logger -- %s.\n", + FUNC_NAME, res_to_cstr(res)); + } + goto error; + } + scmap->logger = &scmap->logger__; + } + + res = setup_palette(scmap, palette); + if(res != RES_OK) goto error; + +exit: + if(out_scmap) *out_scmap = scmap; + return res; +error: + if(scmap) { + SCMAP(ref_put(scmap)); + scmap = NULL; + } + goto exit; +} + +res_T +scmap_ref_get(struct scmap* scmap) +{ + if(!scmap) return RES_BAD_ARG; + ref_get(&scmap->ref); + return RES_OK; +} + +res_T +scmap_ref_put(struct scmap* scmap) +{ + if(!scmap) return RES_BAD_ARG; + ref_put(&scmap->ref, release_scmap); + return RES_OK; +} + diff --git a/src/scmap.h b/src/scmap.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2020 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SCMAP_H +#define SCMAP_H + +#include <rsys/rsys.h> + +/* Library symbol management */ +#if defined(SCMAP_SHARED_BUILD) /* Build shared library */ + #define SCMAP_API extern EXPORT_SYM +#elif defined(SCMAP_STATIC) /* Use/build static library */ + #define SCMAP_API extern LOCAL_SYM +#else /* Use shared library */ + #define SCMAP_API extern IMPORT_SYM +#endif + +/* Helper macro that asserts if the invocation of the scmap function `Func' + * returns an error. One should use this macro on scmap function calls for + * which no explicit error checking is performed */ +#ifndef NDEBUG + #define SCMAP(Func) ASSERT(scmap_ ## Func == RES_OK) +#else + #define SCMA(Func) scmap_ ## Func +#endif + +enum scmap_filter { + SCMAP_FILTER_NEAREST, + SCMAP_FILTER_LINEAR +}; + +struct scmap_palette { + void (*get_color)(const size_t icolor, double color[3], void* context); + size_t ncolors; /* #colors */ + void* context; +}; +static const struct scmap_palette SCMAP_PALETTE_NULL; + +/* Forward declarations */ +struct scmap; +struct logger; +struct mem_allocator; + +BEGIN_DECLS + +/* Builtin palettes */ +SCMAP_API const struct scmap_palette scmap_palette_accent; +SCMAP_API const struct scmap_palette scmap_palette_blues; + +/******************************************************************************* + * Star-ColorMap API + ******************************************************************************/ +SCMAP_API res_T +scmap_create + (struct logger* logger, /* NULL <=> use builtin logger */ + struct mem_allocator* allocator, /* NULL <=> use default allocator */ + const int verbose, /* Verbosity level */ + const struct scmap_palette* palette, + struct scmap** scmap); + +SCMAP_API res_T +scmap_ref_get + (struct scmap* scmap); + +SCMAP_API res_T +scmap_ref_put + (struct scmap* scmap); + +SCMAP_API res_T +scmap_fetch_color + (const struct scmap* scmap, + const double value, + const enum scmap_filter filter, + double color[3]); /* In [0, 1] */ + +END_DECLS + +#endif /* SCMAP_H */ + diff --git a/src/scmap_palettes.c b/src/scmap_palettes.c @@ -0,0 +1,56 @@ +/* Copyright (C) 2020 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "scmap.h" + +static const double accent[] = { + 127.0/255.0, 201.0/255.0, 127.0/255.0, + 190.0/255.0, 174.0/255.0, 212.0/255.0, + 253.0/255.0, 192.0/255.0, 134.0/255.0, + 255.0/255.0, 255.0/255.0, 153.0/255.0, + 56.0/255.0, 108.0/255.0, 176.0/255.0, + 240.0/255.0, 2.0/255.0, 127.0/255.0, + 191.0/255.0, 91.0/255.0, 23.0/255.0, + 102.0/255.0, 102.0/255.0, 102.0/255.0 +}; + +static const double blues[] = { + 247.0/255.0, 251.0/255.0, 255.0/255.0, + 222.0/255.0, 235.0/255.0, 247.0/255.0, + 198.0/255.0, 219.0/255.0, 239.0/255.0, + 158.0/255.0, 202.0/255.0, 225.0/255.0, + 107.0/255.0, 174.0/255.0, 214.0/255.0, + 66.0/255.0, 146.0/255.0, 198.0/255.0, + 33.0/255.0, 113.0/255.0, 181.0/255.0, + 8.0/255.0, 69.0/255.0, 148.0/255.0 +}; + +static INLINE void +get_color(const size_t icolor, double color[3], void* context) +{ + const double* colors = context; + ASSERT(color && context); + color[0] = colors[icolor*3+0]; + color[1] = colors[icolor*3+1]; + color[2] = colors[icolor*3+2]; +} + +const struct scmap_palette scmap_palette_accent = { + get_color, sizeof(accent)/sizeof(double[3]), (void*)accent +}; + +const struct scmap_palette scmap_palette_blues = { + get_color, sizeof(blues)/sizeof(double[3]), (void*)blues +}; diff --git a/src/test_scmap.c b/src/test_scmap.c @@ -0,0 +1,108 @@ +/* Copyright (C) 2020 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "scmap.h" +#include <rsys/logger.h> + +static void +get_white(const size_t icol, double col[3], void* context) +{ + (void)icol, (void)context; + CHK(col); + CHK((intptr_t)context == 0xDECAFBAD); + col[0] = col[1] = col[2] = 1; +} + +static void +get_color(const size_t icol, double col[3], void* context) +{ + const double* color = context; + (void)icol, (void)context; + CHK(col); + col[0] = color[0]; + col[1] = color[1]; + col[2] = color[2]; +} + +static void +log_stream(const char* msg, void* ctx) +{ + ASSERT(msg); + (void)msg, (void)ctx; + printf("%s", msg); +} + +int +main(int argc, char** argv) +{ + struct logger logger; + struct scmap* scmap = NULL; + struct scmap_palette palette = SCMAP_PALETTE_NULL; + double color[3]; + (void)argc, (void)argv; + + CHK(scmap_create(NULL, NULL, 0, NULL, &scmap) == RES_BAD_ARG); + CHK(scmap_create(NULL, NULL, 0, &scmap_palette_accent, NULL) == RES_BAD_ARG); + CHK(scmap_create(NULL, NULL, 0, &scmap_palette_accent, &scmap) == RES_OK); + + CHK(scmap_ref_get(NULL) == RES_BAD_ARG); + CHK(scmap_ref_get(scmap) == RES_OK); + CHK(scmap_ref_put(NULL) == RES_BAD_ARG); + CHK(scmap_ref_put(scmap) == RES_OK); + CHK(scmap_ref_put(scmap) == RES_OK); + + CHK(scmap_create(NULL, &mem_default_allocator, 1, &scmap_palette_accent, + &scmap) == RES_OK); + CHK(scmap_ref_put(scmap) == RES_OK); + + CHK(logger_init(&mem_default_allocator, &logger) == RES_OK); + logger_set_stream(&logger, LOG_OUTPUT, log_stream, NULL); + logger_set_stream(&logger, LOG_ERROR, log_stream, NULL); + logger_set_stream(&logger, LOG_WARNING, log_stream, NULL); + + CHK(scmap_create(&logger, &mem_default_allocator, 0, &scmap_palette_accent, + &scmap) == RES_OK); + CHK(scmap_ref_put(scmap) == RES_OK); + + palette.get_color = get_white; + palette.ncolors = 1 ; + palette.context = (void*)0xDECAFBAD; + + CHK(scmap_create(NULL, NULL, 1, &palette, &scmap) == RES_OK); + CHK(scmap_ref_put(scmap) == RES_OK); + + color[0] = -1; + color[1] = -1; + color[2] = -1; + palette.ncolors = 0; + CHK(scmap_create(NULL, NULL, 1, &palette, &scmap) == RES_BAD_ARG); + palette.ncolors = 1; + palette.get_color = NULL; + CHK(scmap_create(NULL, NULL, 1, &palette, &scmap) == RES_BAD_ARG); + palette.get_color = get_color; + palette.context = color; + CHK(scmap_create(NULL, NULL, 1, &palette, &scmap) == RES_BAD_ARG); + color[0] = 0; + color[1] = 0; + color[2] = 1.1; + CHK(scmap_create(NULL, NULL, 1, &palette, &scmap) == RES_BAD_ARG); + color[2] = 1; + CHK(scmap_create(NULL, NULL, 1, &palette, &scmap) == RES_OK); + CHK(scmap_ref_put(scmap) == RES_OK); + + logger_release(&logger); + CHK(mem_allocated_size() == 0); +return 0; +}