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:
| A | cmake/CMakeLists.txt | | | 87 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scmap.c | | | 272 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scmap.h | | | 91 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scmap_palettes.c | | | 56 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/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;
+}