Add export format options: CSV, JSON, PPM master
authorMartin Lambers <marlam@marlam.de>
Wed, 28 Aug 2019 09:08:58 +0000 (11:08 +0200)
committerMartin Lambers <marlam@marlam.de>
Wed, 28 Aug 2019 09:08:58 +0000 (11:08 +0200)
CMakeLists.txt
cmdline.cpp
colormapwidgets.cpp
colormapwidgets.hpp
export.cpp [new file with mode: 0644]
export.hpp [new file with mode: 0644]
gui.cpp
gui.hpp
testwidget.cpp
testwidget.hpp

index a06f805..4ea5955 100644 (file)
@@ -18,7 +18,7 @@ endif()
 
 find_package(Qt5Widgets QUIET)
 
-add_executable(gencolormap cmdline.cpp colormap.hpp colormap.cpp)
+add_executable(gencolormap cmdline.cpp colormap.hpp colormap.cpp export.hpp export.cpp)
 install(TARGETS gencolormap RUNTIME DESTINATION bin)
 
 if(Qt5Widgets_FOUND)
@@ -26,6 +26,7 @@ if(Qt5Widgets_FOUND)
        add_executable(gencolormap-gui gui.cpp
                colormapwidgets.hpp colormapwidgets.cpp
                 testwidget.hpp testwidget.cpp
+               export.hpp export.cpp
                colormap.hpp colormap.cpp ${GUI_RESOURCES})
        target_link_libraries(gencolormap-gui Qt5::Widgets)
        install(TARGETS gencolormap-gui RUNTIME DESTINATION bin)
index 0d188c5..4d8c753 100644 (file)
@@ -33,6 +33,7 @@ extern char *optarg;
 extern int optind;
 
 #include "colormap.hpp"
+#include "export.hpp"
 
 enum type {
     brewer_seq,
@@ -50,12 +51,19 @@ enum type {
     mcnames
 };
 
+enum format {
+    csv,
+    json,
+    ppm
+};
+
 int main(int argc, char* argv[])
 {
     bool print_version = false;
     bool print_help = false;
-    int type = -1;
-    int n = -1;
+    int format = csv;
+    int type = brewer_seq;
+    int n = 256;
     float hue = -1.0f;
     float divergence = -1.0f;
     float contrast = -1.0f;
@@ -75,6 +83,7 @@ int main(int argc, char* argv[])
     struct option options[] = {
         { "version",     no_argument,       0, 'v' },
         { "help",        no_argument,       0, 'H' },
+        { "format",      required_argument, 0, 'f' },
         { "type",        required_argument, 0, 't' },
         { "n",           required_argument, 0, 'n' },
         { "hue",         required_argument, 0, 'h' },
@@ -95,7 +104,7 @@ int main(int argc, char* argv[])
     };
 
     for (;;) {
-        int c = getopt_long(argc, argv, "vHt:n:h:d:c:s:b:w:l:T:R:r:g:A:O:p:", options, NULL);
+        int c = getopt_long(argc, argv, "vHf:t:n:h:d:c:s:b:w:l:T:R:r:g:A:O:p:", options, NULL);
         if (c == -1)
             break;
         switch (c) {
@@ -105,6 +114,12 @@ int main(int argc, char* argv[])
         case 'H':
             print_help = true;
             break;
+        case 'f':
+            format = (strcmp(optarg, "csv") == 0 ? csv
+                    : strcmp(optarg, "json") == 0 ? json
+                    : strcmp(optarg, "ppm") == 0 ? ppm
+                    : -1);
+            break;
         case 't':
             type = (strcmp(optarg, "brewer-sequential") == 0 ? brewer_seq
                     : strcmp(optarg, "brewer-diverging") == 0 ? brewer_div
@@ -119,7 +134,7 @@ int main(int argc, char* argv[])
                     : strcmp(optarg, "cubehelix") == 0 ? cubehelix
                     : strcmp(optarg, "moreland") == 0 ? moreland
                     : strcmp(optarg, "mcnames") == 0 ? mcnames
-                    : -2);
+                    : -1);
             break;
         case 'n':
             n = atoi(optarg);
@@ -175,6 +190,7 @@ int main(int argc, char* argv[])
 
     if (print_version) {
         printf("gencolormap version 1.0\n"
+                "https://marlam.de/gencolormap\n"
                 "Copyright (C) 2019 Computer Graphics Group, University of Siegen.\n"
                 "Written by Martin Lambers <martin.lambers@uni-siegen.de>.\n"
                 "This is free software under the terms of the MIT/Expat License.\n"
@@ -183,58 +199,65 @@ int main(int argc, char* argv[])
     }
 
     if (print_help) {
-        printf("Usage: %s\n"
-                "  Common options, required for all types:\n"
-                "    -n|--n=N                         Set number of colors in the map\n"
-                "  Brewer-like color maps:\n"
-                "    -t|--type=brewer-sequential       Generate a sequential color map\n"
-                "    -t|--type=brewer-diverging        Generate a diverging color map\n"
-                "    -t|--type=brewer-qualitative      Generate a qualitative color map\n"
-                "    [-h|--hue=H]                      Set default hue in [0,360] degrees\n"
-                "    [-c|--contrast=C]                 Set contrast in [0,1]\n"
-                "    [-s|--saturation=S]               Set saturation in [0,1]\n"
-                "    [-b|--brightness=B]               Set brightness in [0,1]\n"
-                "    [-w|--warmth=W]                   Set warmth in [0,1] for seq. and div. maps\n"
-                "    [-d|--divergence=D]               Set divergence in deg. for div. and qual. maps\n"
-                "  Perceptually linear color maps:\n"
-                "    -t|--type=plsequential-lightness  Sequential map with varying lightness\n"
-                "    -t|--type=plsequential-saturation Sequential map with varying saturation\n"
-                "    -t|--type=plsequential-rainbow    Sequential map with varying hue (rainbow)\n"
-                "    -t|--type=plsequential-blackbody  Sequential map with varying hue (black body)\n"
-                "    -t|--type=pldiverging-lightness   Diverging map with varying lightness\n"
-                "    -t|--type=pldiverging-saturation  Diverging map with varying saturation\n"
-                "    -t|--type=plqualitative-hue       Qualitative map with evenly distributed hue\n"
-                "    [-l|--lightness=L]                Set lightness in [0,1]\n"
-                "    [-s|--saturation=S]               Set saturation in [0,1]\n"
-                "    [-h|--hue=H]                      Set default hue in [0,360] degrees\n"
-                "    [-d|--divergence=D]               Set divergence in deg. for div. and qual. maps\n"
-                "    [-r|--rotations=R]                Set number of rotations for rainbow maps\n"
-                "    [-T|--temperature=T]              Set start temp. in K for blackbody maps\n"
-                "    [-R|--range=R]                    Set temp. range in K for blackbody maps\n"
-                "  CubeHelix color maps:\n"
-                "    -t|--type=cubehelix               Generate a CubeHelix color map\n"
-                "    [-h|--hue=H]                      Set start hue in [0,180] degrees\n"
-                "    [-r|--rotations=R]                Set number of rotations, in (-infty,infty)\n"
-                "    [-s|--saturation=S]               Set saturation, in [0,1]\n"
-                "    [-g|--gamma=G]                    Set gamma correction, in (0,infty)\n"
-                "  Moreland diverging color maps:\n"
-                "    -t|--type=moreland                Generate a Moreland diverging color map\n"
-                "    [-A|--color0=sr,sg,sb             Set the first color as sRGB values in [0,255]\n"
-                "    [-O|--color1=sr,sg,sb             Set the last color as sRGB values in [0,255]\n"
-                "  McNames sequential color maps:\n"
-                "    -t|--type=mcnames                 Generate a McNames sequential color map\n"
-                "    [-p|--periods=P]                  Set the number of periods in (0, infty)\n"
-                "Generates a color map and prints it to standard output as sRGB triplets.\n"
-                "Report bugs to <martin.lambers@uni-siegen.de>.\n", argv[0]);
+        printf("Usage: %s [option...]\n"
+                "Generates a color map and prints it to standard output.\n"
+                "Prints the number of colors that had to be clipped to standard error.\n"
+                "Common options:\n"
+                "  [-f|--format=csv|json|ppm]          Set output format\n"
+                "  [-n|--n=N]                          Set number of colors in the map\n"
+                "Brewer-like color maps:\n"
+                "  [-t|--type=brewer-sequential]       Generate a sequential color map\n"
+                "  [-t|--type=brewer-diverging]        Generate a diverging color map\n"
+                "  [-t|--type=brewer-qualitative]      Generate a qualitative color map\n"
+                "  [-h|--hue=H]                        Set default hue in [0,360] degrees\n"
+                "  [-c|--contrast=C]                   Set contrast in [0,1]\n"
+                "  [-s|--saturation=S]                 Set saturation in [0,1]\n"
+                "  [-b|--brightness=B]                 Set brightness in [0,1]\n"
+                "  [-w|--warmth=W]                     Set warmth in [0,1] for seq. and div. maps\n"
+                "  [-d|--divergence=D]                 Set diverg. in deg for div. and qual. maps\n"
+                "Perceptually linear color maps:\n"
+                "  [-t|--type=plsequential-lightness]  Sequential map, varying lightness\n"
+                "  [-t|--type=plsequential-saturation] Sequential map, varying saturation\n"
+                "  [-t|--type=plsequential-rainbow]    Sequential map, varying hue (rainbow)\n"
+                "  [-t|--type=plsequential-blackbody]  Sequential map, varying hue (black body)\n"
+                "  [-t|--type=pldiverging-lightness]   Diverging map, varying lightness\n"
+                "  [-t|--type=pldiverging-saturation]  Diverging map, varying saturation\n"
+                "  [-t|--type=plqualitative-hue]       Qualitative map, evenly distributed hue\n"
+                "  [-l|--lightness=L]                  Set lightness in [0,1]\n"
+                "  [-s|--saturation=S]                 Set saturation in [0,1]\n"
+                "  [-h|--hue=H]                        Set default hue in [0,360] degrees\n"
+                "  [-d|--divergence=D]                 Set diverg. in deg for div. and qual. maps\n"
+                "  [-r|--rotations=R]                  Set number of rotations for rainbow maps\n"
+                "  [-T|--temperature=T]                Set start temp. in K for black body maps\n"
+                "  [-R|--range=R]                      Set temp. range in K for black body maps\n"
+                "CubeHelix color maps:\n"
+                "  [-t|--type=cubehelix]               Generate a CubeHelix color map\n"
+                "  [-h|--hue=H]                        Set start hue in [0,180] degrees\n"
+                "  [-r|--rotations=R]                  Set number of rotations, in (-infty,infty)\n"
+                "  [-s|--saturation=S]                 Set saturation, in [0,1]\n"
+                "  [-g|--gamma=G]                      Set gamma correction, in (0,infty)\n"
+                "Moreland diverging color maps:\n"
+                "  [-t|--type=moreland]                Generate a Moreland diverging color map\n"
+                "  [-A|--color0=sr,sg,sb]              Set the first color as sRGB in [0,255]\n"
+                "  [-O|--color1=sr,sg,sb]              Set the last color as sRGB in [0,255]\n"
+                "McNames sequential color maps:\n"
+                "  [-t|--type=mcnames]                 Generate a McNames sequential color map\n"
+                "  [-p|--periods=P]                    Set the number of periods in (0, infty)\n"
+                "Defaults: format=csv, n=256, type=brewer-sequential\n"
+                "https://marlam.de/gencolormap\n", argv[0]);
         return 0;
     }
 
-    if (type < 0) {
-        fprintf(stderr, "Invalid or missing option -t|--type.\n");
+    if (format < 0) {
+        fprintf(stderr, "Invalid argument for option -f|--format.\n");
         return 1;
     }
     if (n < 2) {
-        fprintf(stderr, "Invalid or missing option -n|--n.\n");
+        fprintf(stderr, "Invalid argument for option -n|--n.\n");
+        return 1;
+    }
+    if (type < 0) {
+        fprintf(stderr, "Invalid argument for option -t|--type.\n");
         return 1;
     }
     if (hue < 0.0f) {
@@ -412,9 +435,15 @@ int main(int argc, char* argv[])
         break;
     }
 
-    for (int i = 0; i < n; i++) {
-        printf("%d, %d, %d\n", colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]);
+    std::string output;
+    if (format == csv) {
+        output = ColorMap::ToCSV(n, colormap.data());
+    } else if (format == json) {
+        output = ColorMap::ToJSON(n, colormap.data());
+    } else {
+        output = ColorMap::ToPPM(n, colormap.data());
     }
+    fputs(output.c_str(), stdout);
     fprintf(stderr, "%d color(s) were clipped\n", clipped);
 
     return 0;
index ae1a6b0..1e6062e 100644 (file)
@@ -154,19 +154,23 @@ ColorMapWidget::~ColorMapWidget()
 {
 }
 
-QImage ColorMapWidget::colorMapImage(const QVector<QColor>& colormap, int width, int height)
+QImage ColorMapWidget::colorMapImage(const QVector<unsigned char>& colormap, int width, int height)
 {
+    QVector<QColor> qcolormap(colormap.size() / 3);
+    for (int i = 0; i < qcolormap.size(); i++)
+        qcolormap[i] = QColor(colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]);
+
     if (width <= 0)
-        width = colormap.size();
+        width = qcolormap.size();
     if (height <= 0)
-        height = colormap.size();
+        height = qcolormap.size();
     QImage img(width, height, QImage::Format_RGB32);
     bool y_direction = (height > width);
     if (y_direction) {
         for (int y = 0; y < height; y++) {
-            float entry_height = height / static_cast<float>(colormap.size());
+            float entry_height = height / static_cast<float>(qcolormap.size());
             int i = y / entry_height;
-            QRgb rgb = colormap[i].rgb();
+            QRgb rgb = qcolormap[i].rgb();
             QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(height - 1 - y));
             for (int x = 0; x < width; x++)
                 scanline[x] = rgb;
@@ -175,9 +179,9 @@ QImage ColorMapWidget::colorMapImage(const QVector<QColor>& colormap, int width,
         for (int y = 0; y < height; y++) {
             QRgb* scanline = reinterpret_cast<QRgb*>(img.scanLine(y));
             for (int x = 0; x < width; x++) {
-                float entry_width = width / static_cast<float>(colormap.size());
+                float entry_width = width / static_cast<float>(qcolormap.size());
                 int i = x / entry_width;
-                scanline[x] = colormap[i].rgb();
+                scanline[x] = qcolormap[i].rgb();
             }
         }
     }
@@ -186,14 +190,6 @@ QImage ColorMapWidget::colorMapImage(const QVector<QColor>& colormap, int width,
 
 /* Helper functions for ColorMap*Widget */
 
-static QVector<QColor> toQColor(const QVector<unsigned char>& cm)
-{
-    QVector<QColor> colormap(cm.size() / 3);
-    for (int i = 0; i < colormap.size(); i++)
-        colormap[i] = QColor(cm[3 * i + 0], cm[3 * i + 1], cm[3 * i + 2]);
-    return colormap;
-}
-
 static void hideWidgetButPreserveSize(QWidget* widget)
 {
     QSizePolicy sp = widget->sizePolicy();
@@ -286,7 +282,7 @@ void ColorMapBrewerSequentialWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapBrewerSequentialWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapBrewerSequentialWidget::colorMap(int* clipped) const
 {
     int n;
     float h, c, s, b, w;
@@ -295,7 +291,7 @@ QVector<QColor> ColorMapBrewerSequentialWidget::colorMap(int* clipped) const
     int cl = ColorMap::BrewerSequential(n, colormap.data(), h, c, s, b, w);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapBrewerSequentialWidget::reference() const
@@ -403,7 +399,7 @@ void ColorMapBrewerDivergingWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapBrewerDivergingWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapBrewerDivergingWidget::colorMap(int* clipped) const
 {
     int n;
     float h, d, c, s, b, w;
@@ -412,7 +408,7 @@ QVector<QColor> ColorMapBrewerDivergingWidget::colorMap(int* clipped) const
     int cl = ColorMap::BrewerDiverging(n, colormap.data(), h, d, c, s, b, w);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapBrewerDivergingWidget::reference() const
@@ -522,7 +518,7 @@ void ColorMapBrewerQualitativeWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapBrewerQualitativeWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapBrewerQualitativeWidget::colorMap(int* clipped) const
 {
     int n;
     float h, d, c, s, b;
@@ -531,7 +527,7 @@ QVector<QColor> ColorMapBrewerQualitativeWidget::colorMap(int* clipped) const
     int cl = ColorMap::BrewerQualitative(n, colormap.data(), h, d, c, s, b);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapBrewerQualitativeWidget::reference() const
@@ -607,7 +603,7 @@ void ColorMapPLSequentialLightnessWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLSequentialLightnessWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapPLSequentialLightnessWidget::colorMap(int* clipped) const
 {
     int n;
     float s, h;
@@ -616,7 +612,7 @@ QVector<QColor> ColorMapPLSequentialLightnessWidget::colorMap(int* clipped) cons
     int cl = ColorMap::PLSequentialLightness(n, colormap.data(), s, h);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapPLSequentialLightnessWidget::reference() const
@@ -696,7 +692,7 @@ void ColorMapPLSequentialSaturationWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLSequentialSaturationWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapPLSequentialSaturationWidget::colorMap(int* clipped) const
 {
     int n;
     float l, s, h;
@@ -705,7 +701,7 @@ QVector<QColor> ColorMapPLSequentialSaturationWidget::colorMap(int* clipped) con
     int cl = ColorMap::PLSequentialSaturation(n, colormap.data(), l, s, h);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapPLSequentialSaturationWidget::reference() const
@@ -786,7 +782,7 @@ void ColorMapPLSequentialRainbowWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLSequentialRainbowWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapPLSequentialRainbowWidget::colorMap(int* clipped) const
 {
     int n;
     float h, r, s;
@@ -795,7 +791,7 @@ QVector<QColor> ColorMapPLSequentialRainbowWidget::colorMap(int* clipped) const
     int cl = ColorMap::PLSequentialRainbow(n, colormap.data(), h, r, s);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapPLSequentialRainbowWidget::reference() const
@@ -877,7 +873,7 @@ void ColorMapPLSequentialBlackBodyWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLSequentialBlackBodyWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapPLSequentialBlackBodyWidget::colorMap(int* clipped) const
 {
     int n;
     float t, r, s;
@@ -886,7 +882,7 @@ QVector<QColor> ColorMapPLSequentialBlackBodyWidget::colorMap(int* clipped) cons
     int cl = ColorMap::PLSequentialBlackBody(n, colormap.data(), t, r, s);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapPLSequentialBlackBodyWidget::reference() const
@@ -975,7 +971,7 @@ void ColorMapPLDivergingLightnessWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLDivergingLightnessWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapPLDivergingLightnessWidget::colorMap(int* clipped) const
 {
     int n;
     float l, s, h, d;
@@ -984,7 +980,7 @@ QVector<QColor> ColorMapPLDivergingLightnessWidget::colorMap(int* clipped) const
     int cl = ColorMap::PLDivergingLightness(n, colormap.data(), l, s, h, d);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapPLDivergingLightnessWidget::reference() const
@@ -1074,7 +1070,7 @@ void ColorMapPLDivergingSaturationWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLDivergingSaturationWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapPLDivergingSaturationWidget::colorMap(int* clipped) const
 {
     int n;
     float l, s, h, d;
@@ -1083,7 +1079,7 @@ QVector<QColor> ColorMapPLDivergingSaturationWidget::colorMap(int* clipped) cons
     int cl = ColorMap::PLDivergingSaturation(n, colormap.data(), l, s, h, d);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapPLDivergingSaturationWidget::reference() const
@@ -1173,7 +1169,7 @@ void ColorMapPLQualitativeHueWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLQualitativeHueWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapPLQualitativeHueWidget::colorMap(int* clipped) const
 {
     int n;
     float h, d, l, s;
@@ -1182,7 +1178,7 @@ QVector<QColor> ColorMapPLQualitativeHueWidget::colorMap(int* clipped) const
     int cl = ColorMap::PLQualitativeHue(n, colormap.data(), h, d, l, s);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapPLQualitativeHueWidget::reference() const
@@ -1272,7 +1268,7 @@ void ColorMapCubeHelixWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapCubeHelixWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapCubeHelixWidget::colorMap(int* clipped) const
 {
     int n;
     float h, r, s, g;
@@ -1281,7 +1277,7 @@ QVector<QColor> ColorMapCubeHelixWidget::colorMap(int* clipped) const
     int cl = ColorMap::CubeHelix(n, colormap.data(), h, r, s, g);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapCubeHelixWidget::reference() const
@@ -1390,7 +1386,7 @@ void ColorMapMorelandWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapMorelandWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapMorelandWidget::colorMap(int* clipped) const
 {
     int n;
     unsigned char r0, g0, b0, r1, g1, b1;
@@ -1399,7 +1395,7 @@ QVector<QColor> ColorMapMorelandWidget::colorMap(int* clipped) const
     int cl = ColorMap::Moreland(n, colormap.data(), r0, g0, b0, r1, g1, b1);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapMorelandWidget::reference() const
@@ -1471,7 +1467,7 @@ void ColorMapMcNamesWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapMcNamesWidget::colorMap(int* clipped) const
+QVector<unsigned char> ColorMapMcNamesWidget::colorMap(int* clipped) const
 {
     int n;
     float p;
@@ -1480,7 +1476,7 @@ QVector<QColor> ColorMapMcNamesWidget::colorMap(int* clipped) const
     int cl = ColorMap::McNames(n, colormap.data(), p);
     if (clipped)
         *clipped = cl;
-    return toQColor(colormap);
+    return colormap;
 }
 
 QString ColorMapMcNamesWidget::reference() const
index a8ff2da..84e0cf4 100644 (file)
@@ -73,12 +73,12 @@ public:
 
     /* Get the color map corresponding to the current values as a vector of colors.
      * Also return the number of clipped colors unless 'clipped' is NULL. */
-    virtual QVector<QColor> colorMap(int* clipped = NULL) const = 0;
+    virtual QVector<unsigned char> colorMap(int* clipped = NULL) const = 0;
 
     /* Transform a color map to an image of the specified size. If width or
      * height is zero, then it will be set to the number of colors in the
      * color map. */
-    static QImage colorMapImage(const QVector<QColor>& colormap, int width, int height);
+    static QImage colorMapImage(const QVector<unsigned char>& colormap, int width, int height);
 
     /* Get a rich text string containing the relevant literature reference for this method */
     virtual QString reference() const = 0;
@@ -106,7 +106,7 @@ public:
     ~ColorMapBrewerSequentialWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue,
             float& contrast, float& saturation, float& brightness, float& warmth) const;
@@ -132,7 +132,7 @@ public:
     ~ColorMapBrewerDivergingWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& divergence,
             float& contrast, float& saturation, float& brightness, float& warmth) const;
@@ -157,7 +157,7 @@ public:
     ~ColorMapBrewerQualitativeWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& divergence,
             float& contrast, float& saturation, float& brightness) const;
@@ -179,7 +179,7 @@ public:
     ~ColorMapPLSequentialLightnessWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& saturation, float& hue) const;
 };
@@ -201,7 +201,7 @@ public:
     ~ColorMapPLSequentialSaturationWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& lightness, float& saturation, float& hue) const;
 };
@@ -223,7 +223,7 @@ public:
     ~ColorMapPLSequentialRainbowWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& rotations, float& saturation) const;
 };
@@ -245,7 +245,7 @@ public:
     ~ColorMapPLSequentialBlackBodyWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& temperature, float& range, float& saturation) const;
 };
@@ -268,7 +268,7 @@ public:
     ~ColorMapPLDivergingLightnessWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& lightness, float& saturation, float& hue, float& divergence) const;
 };
@@ -291,7 +291,7 @@ public:
     ~ColorMapPLDivergingSaturationWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& lightness, float& saturation, float& hue, float& divergence) const;
 };
@@ -314,7 +314,7 @@ public:
     ~ColorMapPLQualitativeHueWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& divergence, float& lightness, float& saturation) const;
 };
@@ -337,7 +337,7 @@ public:
     ~ColorMapCubeHelixWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& rotations,
             float& saturation, float& gamma) const;
@@ -361,7 +361,7 @@ public:
     ~ColorMapMorelandWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n,
             unsigned char& r0, unsigned char& g0, unsigned char& b0,
@@ -383,7 +383,7 @@ public:
     ~ColorMapMcNamesWidget();
 
     void reset() override;
-    QVector<QColor> colorMap(int* clipped = NULL) const override;
+    QVector<unsigned char> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& p) const;
 };
diff --git a/export.cpp b/export.cpp
new file mode 100644 (file)
index 0000000..f1d954b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019
+ * Computer Graphics Group, University of Siegen
+ * Written by Martin Lambers <martin.lambers@uni-siegen.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "export.hpp"
+
+namespace ColorMap {
+
+std::string ToCSV(int n, const unsigned char* srgb_colormap)
+{
+    std::string s;
+    for (int i = 0; i < n; i++) {
+        s +=  std::to_string(srgb_colormap[3 * i + 0]) + ", "
+            + std::to_string(srgb_colormap[3 * i + 1]) + ", "
+            + std::to_string(srgb_colormap[3 * i + 2]) + '\n';
+    }
+    return s;
+}
+
+std::string ToJSON(int n, const unsigned char* srgb_colormap)
+{
+    std::string s =
+        "[\n"
+        "{\n"
+        "\"ColorSpace\" : \"RGB\",\n"
+        "\"Name\" : \"GenColorMapGenerated\",\n"
+        "\"NanColor\" : [ -1, -1, -1 ],\n"
+        "\"RGBPoints\" : [\n";
+    for (int i = 0; i < n; i++) {
+        s +=  std::to_string(i / float(n - 1)) + ", "
+            + std::to_string(srgb_colormap[3 * i + 0] / 255.0f) + ", "
+            + std::to_string(srgb_colormap[3 * i + 1] / 255.0f) + ", "
+            + std::to_string(srgb_colormap[3 * i + 2] / 255.0f) + (i == n - 1 ? "\n" : ",\n");
+    }
+    s += "]\n}\n]\n";
+    return s;
+}
+
+std::string ToPPM(int n, const unsigned char* srgb_colormap)
+{
+    std::string s = "P3\n"; // magic number for plain PPM
+    s += std::to_string(n) + " 1\n"; // width and height
+    s += "255\n"; // max val
+    for (int i = 0; i < n; i++) {
+        s +=  std::to_string(srgb_colormap[3 * i + 0]) + ' '
+            + std::to_string(srgb_colormap[3 * i + 1]) + ' '
+            + std::to_string(srgb_colormap[3 * i + 2]) + '\n';
+    }
+    return s;
+}
+
+}
diff --git a/export.hpp b/export.hpp
new file mode 100644 (file)
index 0000000..bead114
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019
+ * Computer Graphics Group, University of Siegen
+ * Written by Martin Lambers <martin.lambers@uni-siegen.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef COLORMAP_EXPORT_HPP
+#define COLORMAP_EXPORT_HPP
+
+#include <string>
+
+namespace ColorMap {
+
+// Convert a color map with n sRGB triplets to CSV format
+std::string ToCSV(int n, const unsigned char* srgb_colormap);
+
+// Convert a color map with n sRGB triplets to JSON format
+std::string ToJSON(int n, const unsigned char* srgb_colormap);
+
+// Convert a color map with n sRGB triplets to PPM format
+std::string ToPPM(int n, const unsigned char* srgb_colormap);
+
+}
+
+#endif
diff --git a/gui.cpp b/gui.cpp
index 377f976..bb9379e 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -38,6 +38,7 @@
 
 #include "colormapwidgets.hpp"
 #include "testwidget.hpp"
+#include "export.hpp"
 
 
 GUI::GUI()
@@ -133,6 +134,9 @@ GUI::GUI()
     QAction* file_export_csv_act = new QAction("Export as &CSV...", this);
     connect(file_export_csv_act, SIGNAL(triggered()), this, SLOT(file_export_csv()));
     file_menu->addAction(file_export_csv_act);
+    QAction* file_export_json_act = new QAction("Export as &JSON...", this);
+    connect(file_export_json_act, SIGNAL(triggered()), this, SLOT(file_export_json()));
+    file_menu->addAction(file_export_json_act);
     file_menu->addSeparator();
     QAction* quit_act = new QAction("&Quit...", this);
     quit_act->setShortcut(QKeySequence::Quit);
@@ -145,10 +149,13 @@ GUI::GUI()
     QAction* edit_copy_as_img_act = new QAction("Copy as &image", this);
     connect(edit_copy_as_img_act, SIGNAL(triggered()), this, SLOT(edit_copy_as_img()));
     edit_menu->addAction(edit_copy_as_img_act);
-    QAction* edit_copy_as_txt_act = new QAction("Copy as &text", this);
-    connect(edit_copy_as_txt_act, SIGNAL(triggered()), this, SLOT(edit_copy_as_txt()));
-    edit_copy_as_txt_act->setShortcut(QKeySequence::Copy);
-    edit_menu->addAction(edit_copy_as_txt_act);
+    QAction* edit_copy_as_csv_act = new QAction("Copy as &CSV", this);
+    connect(edit_copy_as_csv_act, SIGNAL(triggered()), this, SLOT(edit_copy_as_csv()));
+    edit_copy_as_csv_act->setShortcut(QKeySequence::Copy);
+    edit_menu->addAction(edit_copy_as_csv_act);
+    QAction* edit_copy_as_json_act = new QAction("Copy as &JSON", this);
+    connect(edit_copy_as_json_act, SIGNAL(triggered()), this, SLOT(edit_copy_as_json()));
+    edit_menu->addAction(edit_copy_as_json_act);
     QMenu* help_menu = menuBar()->addMenu("&Help");
     QAction* help_about_act = new QAction("&About", this);
     connect(help_about_act, SIGNAL(triggered()), this, SLOT(help_about()));
@@ -172,7 +179,7 @@ void GUI::update()
 {
     _reference_label->setText(currentWidget()->reference());
     int clipped;
-    QVector<QColor> colormap = currentWidget()->colorMap(&clipped);
+    QVector<unsigned char> colormap = currentWidget()->colorMap(&clipped);
     _clipped_label->setText(QString("Colors clipped: %1").arg(clipped));
     _colormap_label->setPixmap(QPixmap::fromImage(currentWidget()->colorMapImage(colormap, 32, _colormap_label->height())));
     _test_widget->update(colormap);
@@ -195,13 +202,24 @@ void GUI::file_export_csv()
         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
         QFile file(name);
         if (file.open(QIODevice::WriteOnly)) {
-            QVector<QColor> colormap = currentWidget()->colorMap();
             QTextStream stream(&file);
-            for (int i = 0; i < colormap.size(); i++) {
-                stream << colormap[i].red()   << ", "
-                       << colormap[i].green() << ", "
-                       << colormap[i].blue()  << endl;
-            }
+            QVector<unsigned char> colormap = currentWidget()->colorMap();
+            stream << ColorMap::ToCSV(colormap.size() / 3, colormap.constData()).c_str();
+        }
+        QApplication::restoreOverrideCursor();
+    }
+}
+
+void GUI::file_export_json()
+{
+    QString name = QFileDialog::getSaveFileName();
+    if (!name.isEmpty()) {
+        QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+        QFile file(name);
+        if (file.open(QIODevice::WriteOnly)) {
+            QTextStream stream(&file);
+            QVector<unsigned char> colormap = currentWidget()->colorMap();
+            stream << ColorMap::ToJSON(colormap.size() / 3, colormap.constData()).c_str();
         }
         QApplication::restoreOverrideCursor();
     }
@@ -217,23 +235,23 @@ void GUI::edit_copy_as_img()
     QApplication::clipboard()->setImage(currentWidget()->colorMapImage(currentWidget()->colorMap(), 0, 1));
 }
 
-void GUI::edit_copy_as_txt()
+void GUI::edit_copy_as_csv()
 {
-    QVector<QColor> colormap = currentWidget()->colorMap();
-    QString string;
-    QTextStream stream(&string);
-    for (int i = 0; i < colormap.size(); i++) {
-        stream << colormap[i].red()   << ", "
-               << colormap[i].green() << ", "
-               << colormap[i].blue()  << endl;
-    }
-    QApplication::clipboard()->setText(string);
+    QVector<unsigned char> colormap = currentWidget()->colorMap();
+    QApplication::clipboard()->setText(ColorMap::ToCSV(colormap.size() / 3, colormap.constData()).c_str());
+}
+
+void GUI::edit_copy_as_json()
+{
+    QVector<unsigned char> colormap = currentWidget()->colorMap();
+    QApplication::clipboard()->setText(ColorMap::ToJSON(colormap.size() / 3, colormap.constData()).c_str());
 }
 
 void GUI::help_about()
 {
     QMessageBox::about(this, "About",
-                "<p>gencolormap version 1.0</p>"
+                "<p>gencolormap version 1.0<br>"
+                "   <a href=\"https://marlam.de/gencolormap\">https://marlam.de/gencolormap</a></p>"
                 "<p>Copyright (C) 2019<br>"
                 "   <a href=\"http://www.cg.informatik.uni-siegen.de/\">"
                 "   Computer Graphics Group, University of Siegen</a>.<br>"
diff --git a/gui.hpp b/gui.hpp
index 3b39bba..47943a7 100644 (file)
--- a/gui.hpp
+++ b/gui.hpp
@@ -83,9 +83,11 @@ private slots:
 
     void file_export_png();
     void file_export_csv();
+    void file_export_json();
     void edit_reset();
     void edit_copy_as_img();
-    void edit_copy_as_txt();
+    void edit_copy_as_csv();
+    void edit_copy_as_json();
     void help_about();
 };
 
index 8c60d32..5b4958c 100644 (file)
@@ -24,6 +24,7 @@
 #include <cmath>
 
 #include <QGuiApplication>
+#include <QColor>
 
 #include "testwidget.hpp"
 
@@ -38,8 +39,7 @@ static const float twopi = 2.0 * M_PI;
 ColorMapTestWidget::ColorMapTestWidget() : QLabel()
 {
     setMinimumSize(W * qApp->devicePixelRatio(), H * qApp->devicePixelRatio());
-    QVector<QColor> initial_colormap;
-    initial_colormap.append(QColor(0, 0, 0));
+    QVector<unsigned char> initial_colormap(3, 0);
     update(initial_colormap);
 }
 
@@ -47,7 +47,7 @@ ColorMapTestWidget::~ColorMapTestWidget()
 {
 }
 
-void ColorMapTestWidget::update(const QVector<QColor>& colormap)
+void ColorMapTestWidget::update(const QVector<unsigned char>& colormap)
 {
     QImage img(W * qApp->devicePixelRatio(), H * qApp->devicePixelRatio(), QImage::Format_RGB32);
     for (int y = 0; y < img.height(); y++) {
@@ -60,12 +60,12 @@ void ColorMapTestWidget::update(const QVector<QColor>& colormap)
             float modulation = 0.05f * std::sin(W / 8 * twopi * u);
             float value = ramp + v * v * modulation;
             // Applying colormap
-            int i = std::round(value * (colormap.size() - 1));
+            int i = std::round(value * (colormap.size() / 3 - 1));
             if (i < 0)
                 i = 0;
-            else if (i >= colormap.size())
-                i = colormap.size() - 1;
-            scanline[x] = colormap[i].rgb();
+            else if (i >= colormap.size() / 3)
+                i = colormap.size() / 3 - 1;
+            scanline[x] = QColor(colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]).rgb();
         }
     }
     setPixmap(QPixmap::fromImage(img));
index a1ead4d..adaec66 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Computer Graphics Group, University of Siegen
+ * Copyright (C) 2016, 2019 Computer Graphics Group, University of Siegen
  * Written by Martin Lambers <martin.lambers@uni-siegen.de>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -27,8 +27,6 @@
 #include <QLabel>
 #include <QVector>
 
-class QColor;
-
 class ColorMapTestWidget : public QLabel
 {
 Q_OBJECT
@@ -37,7 +35,7 @@ public:
     ColorMapTestWidget();
     ~ColorMapTestWidget();
 
-    void update(const QVector<QColor>& colormap);
+    void update(const QVector<unsigned char>& colormap);
 };
 
 #endif