diff --git a/CHANGELOG.md b/CHANGELOG.md index 81cf536..7b55221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Le format est basé sur [Keep a Changelog](https://keepachangelog.com/) et ce pr ## [Unreleased] +### Added + +- `Colorize` : Ajout d'un style colorize permettant de remplacer une couleur en une autre avec une possibilité de transparence. + ## [3.0.0] - 2026-03-12 ### Added diff --git a/include/rok4/style/Colorize.h b/include/rok4/style/Colorize.h new file mode 100644 index 0000000..fac5e1a --- /dev/null +++ b/include/rok4/style/Colorize.h @@ -0,0 +1,109 @@ +/* + * Copyright © (2011) Institut national de l'information + * géographique et forestière + * + * Géoportail SAV + * + * This software is a computer program whose purpose is to publish geographic + * data using OGC WMS and WMTS protocol. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * + * knowledge of the CeCILL-C license and that you accept its terms. + */ + + /** + * \file Colorize.h + ** \~french + * \brief D�finition de la classe Colorize + ** \~english + * \brief Define class Colorize + */ + + +#pragma once + +#include "rok4/utils/Configuration.h" + +#include +#include +#include +#include + +class Colorize : public Configuration +{ +public: + /** \~french + * \brief tolerance : seuil de tolérance pour gérer les teintes de blanc plus larges + ** \~english + * \brief tolerance : tolerance threshold for managing wider shades of white + */ + int tolerance; + + /** \~french + * \brief source : valeur visée en entrée + ** \~english + * \brief source : input target value + */ + std::vector source; + + /** \~french + * \brief destination : valeur visée en sortie + ** \~english + * \brief destination : output target value + */ + std::vector destination; + + /** \~french + * \brief noData : valeur de nodata pour l'image source + ** \~english + * \brief noData : value of nodata for the source image + */ + std::vector input_nodata_value; + + /** \~french + * \brief noData : valeur de nodata pour le colorize + ** \~english + * \brief noData : value of nodata for the colorize + */ + float colorize_nodata_value; + + /** + * \~french + * \brief Constructeurs avec des arguments + * \~english + * \brief Constructor with arguments + */ + Colorize(json11::Json doc); + + /** + * \~french + * \brief Destructeur + * \~english + * \brief Destructor + */ + virtual ~Colorize(); +}; diff --git a/include/rok4/style/Style.h b/include/rok4/style/Style.h index f1667c6..8481c67 100644 --- a/include/rok4/style/Style.h +++ b/include/rok4/style/Style.h @@ -56,6 +56,7 @@ class Style; #include "rok4/style/Estompage.h" #include "rok4/style/Aspect.h" #include "rok4/style/Terrainrgb.h" +#include "rok4/style/Colorize.h" #include "rok4/enums/Interpolation.h" #include "rok4/utils/Configuration.h" #include "rok4/utils/StoragePool.h" @@ -169,6 +170,11 @@ private : * \~english \brief Define wether the server must compute a RGB terrain */ Terrainrgb* terrainrgb; + /** + * \~french \brief Définit si un calcul de white to alpha doit être appliqué + * \~english \brief Define wether the server must compute a white to alpha + */ + Colorize* colorize; /** * \~french \brief Valeur de nodata attendue dans les données en entrée @@ -226,7 +232,7 @@ private : * \~english \brief Style is allowed ? */ bool handle (int spp) { - if (estompage_defined() || pente_defined() || aspect_defined() || terrainrgb_defined()) { + if (estompage_defined() || pente_defined() || aspect_defined() || terrainrgb_defined() || colorize_defined()) { return (spp == 1); } else { return true; @@ -258,6 +264,14 @@ private : return orig_channels; } } + else if (colorize_defined()){ + if (orig_channels ==3 || orig_channels ==4){ + return colorize->destination.size(); + } + else { + return orig_channels; + } + } else { if (estompage_defined() || pente_defined() || aspect_defined()) { return 1; @@ -274,7 +288,7 @@ private : * \~english \brief Which sample format after style */ SampleFormat::eSampleFormat get_sample_format (SampleFormat::eSampleFormat sf) { - if ((palette && ! palette->is_empty()) || terrainrgb_defined()) { + if ((palette && ! palette->is_empty()) || terrainrgb_defined() || colorize_defined()) { return SampleFormat::UINT8; } else { return sf; @@ -314,7 +328,7 @@ private : return false; } - if (estompage_defined() || pente_defined() || aspect_defined() || terrainrgb_defined()) { + if (estompage_defined() || pente_defined() || aspect_defined() || terrainrgb_defined() || colorize_defined()) { return false; } else { return true; @@ -475,6 +489,27 @@ private : inline Terrainrgb* get_terrainrgb() { return terrainrgb; } + + /** + * \~french + * \brief Return vrai si le style est un white to alpha + * \return bool + * \~english + * \brief Return true if the style is an white to alpha + * \return bool + */ + inline bool colorize_defined() { + return (colorize != 0); + } + /** + * \~french + * \brief Retourne le white to alpha + * \~english + * \brief Return white to alpha + */ + inline Colorize* get_colorize() { + return colorize; + } /** diff --git a/src/image/StyledImage.cpp b/src/image/StyledImage.cpp index 5c355a5..543c184 100644 --- a/src/image/StyledImage.cpp +++ b/src/image/StyledImage.cpp @@ -169,6 +169,14 @@ StyledImage::StyledImage(Image *input_image, Style *input_style, int offset) : I } } + else if (style->colorize_defined()) { + if (source_image->get_channels() == 3 || source_image->get_channels() == 4) { + channels = style->get_colorize()->destination.size(); + } else { + channels = input_image->get_channels(); + } + } + if (style->palette_defined()){ // Il n'y aura application de la palette et modification des canaux que si // - la palette n'est pas nulle et pas vide @@ -489,6 +497,138 @@ int StyledImage::_getline(T *buffer, int line) { space = width * sizeof ( T ) * channels; } + else if (style->colorize_defined()) { + Colorize* wta = style->get_colorize(); + switch ( channels ) { + case 3: + if (source_image->get_channels()==3){ + for (int i = 0; i < source_image->get_width() ; i++ ) { + //image de départ à 3 canaux pour une arrivée en 3 canaux + int red = *(source+i*3); + int green = *(source+i*3+1); + int blue = *(source+i*3+2); + red = red < 0 ? 0 : (red > 255 ? 255 : red); + green = green < 0 ? 0 : (green > 255 ? 255 : green); + blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue); + int s_red = wta->source[0]; + int s_green = wta->source[1]; + int s_blue = wta->source[2]; + + if (red>=s_red-wta->tolerance && red<=s_red+wta->tolerance && + green>=s_green-wta->tolerance && green<=s_green+wta->tolerance && + blue>=s_blue-wta->tolerance && blue<=s_blue+wta->tolerance){ + * ( buffer+i*3 ) = (T) wta->destination[0]; + * ( buffer+i*3+1 ) = (T) wta->destination[1]; + * ( buffer+i*3+2 ) = (T) wta->destination[2]; + } + else{ + * ( buffer+i*3 ) = (T) red; + * ( buffer+i*3+1 ) = (T) green; + * ( buffer+i*3+2 ) = (T) blue; + } + } + } + if (source_image->get_channels()==4){ + for (int i = 0; i < source_image->get_width() ; i++ ) { + //image de départ à 4 canaux pour une arrivée en 3 canaux + int red = *(source+i*4); + int green = *(source+i*4+1); + int blue = *(source+i*4+2); + int alpha = *(source+i*4+3); + red = red < 0 ? 0 : (red > 255 ? 255 : red); + green = green < 0 ? 0 : (green > 255 ? 255 : green); + blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue); + alpha = alpha < 0 ? 0 : (alpha > 255 ? 255 : alpha); + int s_red = wta->source[0]; + int s_green = wta->source[1]; + int s_blue = wta->source[2]; + int s_alpha = wta->source[3]; + + if (red>=s_red-wta->tolerance && red<=s_red+wta->tolerance && + green>=s_green-wta->tolerance && green<=s_green+wta->tolerance && + blue>=s_blue-wta->tolerance && blue<=s_blue+wta->tolerance && + alpha>=s_alpha-wta->tolerance && alpha<=s_alpha+wta->tolerance){ + * ( buffer+i*3 ) = (T) wta->destination[0]; + * ( buffer+i*3+1 ) = (T) wta->destination[1]; + * ( buffer+i*3+2 ) = (T) wta->destination[2]; + } + else{ + * ( buffer+i*3 ) = (T) red; + * ( buffer+i*3+1 ) = (T) green; + * ( buffer+i*3+2 ) = (T) blue; + } + } + } + break; + case 4: + if (source_image->get_channels()==3){ + for (int i = 0; i < source_image->get_width() ; i++ ) { + //image de départ à 3 canaux pour une arrivée en 4 canaux + int red = *(source+i*3); + int green = *(source+i*3+1); + int blue = *(source+i*3+2); + red = red < 0 ? 0 : (red > 255 ? 255 : red); + green = green < 0 ? 0 : (green > 255 ? 255 : green); + blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue); + int s_red = wta->source[0]; + int s_green = wta->source[1]; + int s_blue = wta->source[2]; + + if (red>=s_red-wta->tolerance && red<=s_red+wta->tolerance && + green>=s_green-wta->tolerance && green<=s_green+wta->tolerance && + blue>=s_blue-wta->tolerance && blue<=s_blue+wta->tolerance){ + * ( buffer+i*4 ) = (T) wta->destination[0]; + * ( buffer+i*4+1 ) = (T) wta->destination[1]; + * ( buffer+i*4+2 ) = (T) wta->destination[2]; + * ( buffer+i*4+3 ) = (T) wta->destination[3]; + } + else{ + * ( buffer+i*4 ) = (T) red; + * ( buffer+i*4+1 ) = (T) green; + * ( buffer+i*4+2 ) = (T) blue; + * ( buffer+i*4+3 ) = (T) 255; + } + } + } + if (source_image->get_channels()==4){ + for (int i = 0; i < source_image->get_width() ; i++ ) { + //image de départ à 4 canaux pour une arrivée en 4 canaux + int red = *(source+i*4); + int green = *(source+i*4+1); + int blue = *(source+i*4+2); + int alpha = *(source+i*4+3); + red = red < 0 ? 0 : (red > 255 ? 255 : red); + green = green < 0 ? 0 : (green > 255 ? 255 : green); + blue = blue < 0 ? 0 : (blue > 255 ? 255 : blue); + alpha = alpha < 0 ? 0 : (alpha > 255 ? 255 : alpha); + int s_red = wta->source[0]; + int s_green = wta->source[1]; + int s_blue = wta->source[2]; + int s_alpha = wta->source[3]; + + if (red>=s_red-wta->tolerance && red<=s_red+wta->tolerance && + green>=s_green-wta->tolerance && green<=s_green+wta->tolerance && + blue>=s_blue-wta->tolerance && blue<=s_blue+wta->tolerance && + alpha>=s_alpha-wta->tolerance && alpha<=s_alpha+wta->tolerance){ + * ( buffer+i*4 ) = (T) wta->destination[0]; + * ( buffer+i*4+1 ) = (T) wta->destination[1]; + * ( buffer+i*4+2 ) = (T) wta->destination[2]; + * ( buffer+i*4+3 ) = (T) wta->destination[3]; + } + else{ + * ( buffer+i*4 ) = (T) red; + * ( buffer+i*4+1 ) = (T) green; + * ( buffer+i*4+2 ) = (T) blue; + * ( buffer+i*4+3 ) = (T) alpha; + } + } + } + break; + } + + space = width * sizeof ( T ) * channels; + } + if (style->palette_defined()){ switch ( channels ) { case 4: @@ -541,6 +681,16 @@ StyledImage *StyledImage::create(Image *input_image, Style *input_style) { return NULL; } } + if (input_style->colorize_defined()){ + if (input_image->get_channels()!=3 && input_image->get_channels()!=4){ + BOOST_LOG_TRIVIAL(error)<<"Ce style ne s'applique que sur une image source à trois ou quatre canaux"; + return NULL; + } + if (input_style->palette_defined()){ + BOOST_LOG_TRIVIAL(error)<<"Le style colorize n'est pas compatible avec une palette"; + return NULL; + } + } return new StyledImage(input_image,input_style,offset); } @@ -570,6 +720,9 @@ void StyledImage::print() { if (style->terrainrgb_defined()){ BOOST_LOG_TRIVIAL(info) << "--------- Terrainrgb -----------" ; } + if (style->colorize_defined()){ + BOOST_LOG_TRIVIAL(info) << "--------- Colorize -----------" ; + } if (style->palette_defined()){ BOOST_LOG_TRIVIAL(info) << "--------- Palette -----------" ; } diff --git a/src/style/Colorize.cpp b/src/style/Colorize.cpp new file mode 100644 index 0000000..f2877b8 --- /dev/null +++ b/src/style/Colorize.cpp @@ -0,0 +1,79 @@ +/* + * Copyright © (2011) Institut national de l'information + * géographique et forestière + * + * Géoportail SAV + * + * This software is a computer program whose purpose is to publish geographic + * data using OGC WMS and WMTS protocol. + * + * This software is governed by the CeCILL-C license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/ or redistribute the software under the terms of the CeCILL-C + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * + * knowledge of the CeCILL-C license and that you accept its terms. + */ + + /** + * \file Colorize.cpp + * \~french + * \brief Implémentation de la classe Colorize pour rendre transparent les pixels blancs. + * \~english + * \brief Implement the Colorize Class handling transparency for white pixels definition. + */ + +#include "style/Colorize.h" +#include +#include "zlib.h" +#include +#include "byteswap.h" +#include + +Colorize::Colorize(json11::Json doc) : Configuration() +{ + + if (doc["tolerance"].is_number()) { + tolerance = doc["tolerance"].number_value(); + } else { + tolerance = 0; + } + if (doc["source"].is_array()) { + for (json11::Json v : doc["source"].array_items()) { + source.push_back(v.number_value()); + } + } else { + source = {255,255,255}; + } + if (doc["destination"].is_array()) { + for (json11::Json v : doc["destination"].array_items()) { + destination.push_back(v.number_value()); + } + } else { + destination = {255,255,255,0}; + } + input_nodata_value=source; +} + +Colorize::~Colorize() { +} diff --git a/src/style/Style.cpp b/src/style/Style.cpp index 2d0ef49..6d562e0 100644 --- a/src/style/Style.cpp +++ b/src/style/Style.cpp @@ -61,6 +61,7 @@ bool Style::parse(json11::Json& doc) { aspect = 0; estompage = 0; palette = 0; + colorize = 0; terrainrgb = 0; input_nodata_value = NULL; @@ -147,7 +148,7 @@ bool Style::parse(json11::Json& doc) { } if (doc["terrainrgb"].is_object()) { - if (estompage != 0 || pente != 0 || aspect !=0 || palette !=0) { + if (estompage != 0 || pente != 0 || aspect !=0 || palette !=0 || colorize !=0) { error_message = "Style " + id + " define exposition, estompage, pente or palette rules"; return false; } @@ -157,6 +158,18 @@ bool Style::parse(json11::Json& doc) { return false; } } + + if (doc["colorize"].is_object()) { + if (estompage != 0 || pente != 0 || aspect !=0 || palette !=0 || terrainrgb !=0) { + error_message = "Style " + id + " define exposition, estompage, pente or palette rules"; + return false; + } + colorize = new Colorize(doc["colorize"].object_items()); + if (! colorize->is_ok()) { + error_message = "Colorize issue for style " + id + ": " + colorize->get_error_message(); + return false; + } + } return true; } @@ -167,6 +180,7 @@ Style::Style ( std::string path ) : Configuration(path) { palette = 0; aspect = 0; terrainrgb = 0; + colorize = 0; input_nodata_value = NULL; output_nodata_value = NULL; @@ -237,6 +251,15 @@ Style::Style ( std::string path ) : Configuration(path) { else if (terrainrgb_defined()) { input_nodata_value = new int[1]; input_nodata_value[0] = (int) terrainrgb->input_nodata_value; + } + else if (colorize_defined()) { + int i = colorize->input_nodata_value.size(); + input_nodata_value = new int[i]; + + for (int j = 0; j < i; ++j){ + input_nodata_value[j] = colorize->input_nodata_value[j]; + } + } else if (palette && ! palette->is_empty()) { input_nodata_value = new int[1]; @@ -277,6 +300,14 @@ Style::Style ( std::string path ) : Configuration(path) { output_nodata_value[1] = 0; output_nodata_value[2] = 0; } + else if (colorize_defined()) { + int i = colorize->input_nodata_value.size(); + output_nodata_value = new int[i]; + + for (int j = 0; j < i; ++j){ + output_nodata_value[j] = colorize->destination[j]; + } + } } Style::~Style() { @@ -295,6 +326,9 @@ Style::~Style() { if (terrainrgb != 0) { delete terrainrgb; } + if (colorize != 0) { + delete colorize; + } if (input_nodata_value != NULL) { delete[] input_nodata_value; }