summaryrefslogtreecommitdiffstats
path: root/gis/qmapshack/rgb2pct.patch
blob: 92c5df87762fd89b081201a1fe9b1ced6e835f68 (plain)
From 763cfc149566325cce9e4690cb7b5f986048f86a Mon Sep 17 00:00:00 2001
From: Oliver Eichler <oliver.eichler@dspsolutions.de>
Date: Thu, 12 Sep 2019 20:32:06 +0200
Subject: [PATCH] [QMS-3] Add qmt_rgb2pct from former sub-repo

---
 src/qmt_rgb2pct/CApp.cpp                 | 280 +++++++++++++++++++++++
 src/qmt_rgb2pct/CApp.h                   |  55 +++++
 src/qmt_rgb2pct/CMakeLists.txt           | 117 ++++++++++
 src/qmt_rgb2pct/README.md                |   5 +
 src/qmt_rgb2pct/locale/qmt_rgb2pct.ts    | 126 ++++++++++
 src/qmt_rgb2pct/locale/qmt_rgb2pct_de.ts | 127 ++++++++++
 src/qmt_rgb2pct/main.cpp                 | 155 +++++++++++++
 src/qmt_rgb2pct/version.h                |  33 +++
 8 files changed, 898 insertions(+)
 create mode 100644 src/qmt_rgb2pct/CApp.cpp
 create mode 100644 src/qmt_rgb2pct/CApp.h
 create mode 100644 src/qmt_rgb2pct/CMakeLists.txt
 create mode 100644 src/qmt_rgb2pct/README.md
 create mode 100644 src/qmt_rgb2pct/locale/qmt_rgb2pct.ts
 create mode 100644 src/qmt_rgb2pct/locale/qmt_rgb2pct_de.ts
 create mode 100644 src/qmt_rgb2pct/main.cpp
 create mode 100644 src/qmt_rgb2pct/version.h

diff --git a/src/qmt_rgb2pct/CApp.cpp b/src/qmt_rgb2pct/CApp.cpp
new file mode 100644
index 00000000..993ed759
--- /dev/null
+++ b/src/qmt_rgb2pct/CApp.cpp
@@ -0,0 +1,280 @@
+/**********************************************************************************************
+    Copyright (C) 2018 Oliver Eichler oliver.eichler@gmx.de
+
+    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 "CApp.h"
+
+#include <gdal_alg.h>
+#include <gdal_priv.h>
+#include <iostream>
+
+const GDALColorEntry CApp::noColor = {255,255,255,0};
+
+void printStdoutQString(const QString& str)
+{
+    QByteArray array = str.toUtf8();
+    printf("%s", array.data());
+    printf("\n");
+}
+
+void printStderrQString(const QString& str)
+{
+    QByteArray array = str.toUtf8();
+    fprintf(stderr, "%s", array.data());
+    fprintf(stderr, "\n");
+}
+
+
+
+CApp::CApp(qint32 ncolors, const QString& pctFilename, const QString &sctFilename, const QString &srcFilename, const QString &tarFilename)
+    : ncolors(ncolors)
+    , pctFilename(pctFilename)
+    , sctFilename(sctFilename)
+    , srcFilename(srcFilename)
+    , tarFilename(tarFilename)
+{
+    GDALAllRegister();
+}
+
+qint32 CApp::exec()
+{
+    qint32 res = 0;
+    GDALColorTable * ct = nullptr;
+    GDALDataset * dsSrc = nullptr;
+    try
+    {
+        dsSrc = (GDALDataset*)GDALOpenShared(srcFilename.toUtf8(),GA_ReadOnly);
+        if(dsSrc == nullptr)
+        {
+            throw tr("Failed to open source file.");
+        }
+
+        if(dsSrc->GetRasterCount() < 3 || dsSrc->GetRasterCount() > 4)
+        {
+            throw tr("Raster band count of source file must be either 3 or 4.");
+        }
+
+        if(QFile(tarFilename).exists())
+        {
+            QFile::remove(tarFilename);
+        }
+
+        ct = createColorTable(ncolors, pctFilename, dsSrc);
+        saveColorTable(ct, sctFilename);
+        ditherMap(dsSrc, tarFilename, ct);
+    }
+    catch(const QString& msg)
+    {
+        printStderrQString(msg);
+        res = -1;
+    }
+
+
+    GDALClose(dsSrc);
+    delete ct;
+    return res;
+}
+
+GDALColorTable * CApp::createColorTable(qint32 ncolors, const QString& pctFilename, GDALDataset * dataset)
+{
+    GDALColorTable * ct = nullptr;
+    try
+    {
+        if(pctFilename.isEmpty())
+        {
+            ct = (GDALColorTable*)GDALCreateColorTable(GPI_RGB);
+
+            printStdoutQString(tr("Calculate optimal color table from source file"));
+
+            int ok = GDALComputeMedianCutPCT(dataset->GetRasterBand(1),
+                                             dataset->GetRasterBand(2),
+                                             dataset->GetRasterBand(3),
+                                             nullptr,
+                                             ncolors,
+                                             ct,
+                                             GDALTermProgress,
+                                             0
+                                             );
+
+            if(ok != CE_None)
+            {
+                throw tr("Failed to create color table.");
+            }
+        }
+        else
+        {
+            GDALDataset * dsPct = (GDALDataset*)GDALOpenShared(pctFilename.toUtf8(),GA_ReadOnly);
+            if(dsPct == nullptr)
+            {
+                throw tr("Failed to open file with palette.");
+            }
+
+            GDALRasterBand * band = (GDALRasterBand*)dsPct->GetRasterBand(1);
+
+            if((dsPct->GetRasterCount() != 1) || (band->GetColorInterpretation() != GCI_PaletteIndex))
+            {
+                GDALClose(dsPct);
+                throw tr("Palette file does not have a single band with a color table");
+            }
+
+            int ok = 0;
+            band->GetNoDataValue(&ok);
+
+            if(ok || band->GetColorTable()->GetColorEntryCount() > 255)
+            {
+                GDALClose(dsPct);
+                throw tr("The color table must not contain a \"no data\" value and it's size must not exceed 255 colors.");
+            }
+
+            ct = dsPct->GetRasterBand(1)->GetColorTable()->Clone();
+        }
+    }
+    catch(const QString& msg)
+    {
+        delete ct;
+        throw msg;
+    }
+    return ct;
+}
+
+void CApp::saveColorTable(GDALColorTable * ct, QString& sctFilename)
+{
+    if(sctFilename.isEmpty())
+    {
+        return;
+    }
+
+    if(!sctFilename.endsWith(".vrt"))
+    {
+        sctFilename += ".vrt";
+    }
+
+    QByteArray buf = sctFilename.toUtf8();
+    printStdoutQString(tr("Save color table to: %1").arg(buf.data()));
+
+    GDALDriverManager * drvman  = GetGDALDriverManager();
+    GDALDriver * driver         = drvman->GetDriverByName("VRT");
+    GDALDataset * dataset       = driver->Create(sctFilename.toUtf8(), 1, 1, 1, GDT_Byte, {});
+
+    dataset->GetRasterBand(1)->SetColorInterpretation(GCI_PaletteIndex);
+    dataset->GetRasterBand(1)->SetColorTable(ct);
+
+    dataset->FlushCache();
+    GDALClose(dataset);
+}
+
+void CApp::ditherMap(GDALDataset * dsSrc, const QString& tarFilename, GDALColorTable *ct)
+{
+    if(tarFilename.isEmpty())
+    {
+        return;
+    }
+
+    qint32 xsize = dsSrc->GetRasterBand(1)->GetXSize();
+    qint32 ysize = dsSrc->GetRasterBand(1)->GetYSize();
+
+    GDALDriverManager * drvman  = nullptr;
+    GDALDriver * driver         = nullptr;
+    GDALDataset * dataset       = nullptr;
+
+    try
+    {
+        const char * cargs[] = {"TILED=YES","COMPRESS=LZW", 0};
+        drvman  = GetGDALDriverManager();
+        driver  = drvman->GetDriverByName("GTiff");
+        dataset = driver->Create(tarFilename.toUtf8(), xsize, ysize, 1, GDT_Byte, (char**)cargs);
+
+        if(dataset == nullptr)
+        {
+            throw tr("Failed to create target file.");
+        }
+
+        dataset->GetRasterBand(1)->SetColorTable(ct);
+        dataset->GetRasterBand(1)->SetNoDataValue(ct->GetColorEntryCount());
+        dataset->SetProjection(dsSrc->GetProjectionRef());
+
+        double adfGeoTransform[6] = {0};
+        dsSrc->GetGeoTransform(adfGeoTransform);
+        dataset->SetGeoTransform(adfGeoTransform);
+
+        printStdoutQString(tr("Dither source file to target file"));
+        int res = GDALDitherRGB2PCT(dsSrc->GetRasterBand(1),
+                                    dsSrc->GetRasterBand(2),
+                                    dsSrc->GetRasterBand(3),
+                                    dataset->GetRasterBand(1),
+                                    ct,
+                                    GDALTermProgress,
+                                    0
+                                    );
+        if(res != CE_None)
+        {
+            throw tr("Failed to dither file.");
+        }
+
+        if(dsSrc->GetRasterCount() == 3)
+        {
+            return;
+        }
+
+        GDALRasterBand * alpha = dsSrc->GetRasterBand(4);
+        GDALRasterBand * band  = dataset->GetRasterBand(1);
+
+        QByteArray buffer1(xsize, 0);
+        QByteArray buffer2(xsize, 0);
+
+        quint8 nodata = band->GetNoDataValue();
+        printStdoutQString(tr("Apply alpha channel as no data value to target file"));
+        for(int y = 0; y < ysize; y++)
+        {
+            GDALTermProgress(double(xsize * y)/(xsize*ysize),0,0);
+            res = alpha->RasterIO(GF_Read, 0, y, xsize, 1, buffer1.data(), xsize, 1, GDT_Byte, 0, 0);
+            if(res != CE_None)
+            {
+                throw tr("Failed to read from alpha channel.");
+            }
+
+            res = band->RasterIO(GF_Read, 0, y, xsize, 1, buffer2.data(), xsize, 1, GDT_Byte, 0, 0);
+            if(res != CE_None)
+            {
+                throw tr("Failed to read from target file.");
+            }
+
+            for(int x = 0; x < xsize; x++)
+            {
+                if(buffer1[x] != char(0xFF))
+                {
+                    buffer2[x] = nodata;
+                }
+            }
+
+            res = band->RasterIO(GF_Write, 0, y, xsize, 1, buffer2.data(), xsize, 1, GDT_Byte, 0, 0);
+            if(res != CE_None)
+            {
+                throw tr("Failed to write to target file.");
+            }
+        }
+        GDALTermProgress(1.0,0,0);
+    }
+    catch(const QString& msg)
+    {
+        GDALClose(dataset);
+        throw msg;
+    }
+
+    dataset->FlushCache();
+    GDALClose(dataset);
+}
diff --git a/src/qmt_rgb2pct/CApp.h b/src/qmt_rgb2pct/CApp.h
new file mode 100644
index 00000000..e2e0f7ca
--- /dev/null
+++ b/src/qmt_rgb2pct/CApp.h
@@ -0,0 +1,55 @@
+/**********************************************************************************************
+    Copyright (C) 2018 Oliver Eichler oliver.eichler@gmx.de
+
+    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 CAPP_H
+#define CAPP_H
+
+#include <QtCore>
+#include <gdal.h>
+
+class GDALColorTable;
+class GDALDataset;
+
+class CApp
+{
+    Q_DECLARE_TR_FUNCTIONS(CApp)
+public:
+    CApp(qint32 ncolors, const QString& pctFilename, const QString& sctFilename, const QString& srcFilename, const QString& tarFilename);
+    virtual ~CApp() = default;
+
+    qint32 exec();
+
+private:
+    static GDALColorTable * createColorTable(qint32 ncolors, const QString& pctFilename, GDALDataset *dataset);
+    static void saveColorTable(GDALColorTable *ct, QString &sctFilename);
+    static void ditherMap(GDALDataset * dsSrc, const QString& tarFilename, GDALColorTable *ct);
+
+    qint32 ncolors = 0;
+    QString pctFilename;
+    QString sctFilename;
+    QString srcFilename;
+    QString tarFilename;
+
+    static const GDALColorEntry noColor;
+};
+
+void printStdoutQString(const QString& str);
+void printStderrQString(const QString& str);
+
+#endif //CAPP_H
+
diff --git a/src/qmt_rgb2pct/CMakeLists.txt b/src/qmt_rgb2pct/CMakeLists.txt
new file mode 100644
index 00000000..c763595e
--- /dev/null
+++ b/src/qmt_rgb2pct/CMakeLists.txt
@@ -0,0 +1,117 @@
+# Prevent custom commands/targets outputs to be deleted by make clean
+# We need this to prevent .ts files from being deleted with make clean, when
+# UPDATE_TRANSLATIONS=ON
+# WARNING: Only works with Makefile generator.
+set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM TRUE)
+# Find includes in corresponding build directories
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+# Instruct CMake to run moc automatically when needed.
+set(CMAKE_AUTOMOC ON)
+
+###############################################################################################
+# Setup application name and version tags
+###############################################################################################
+
+set(APPLICATION_NAME qmt_rgb2pct)
+set(RGB2PCT_VERSION_MAJOR 1)
+set(RGB2PCT_VERSION_MINOR 0)
+set(RGB2PCT_VERSION_PATCH 0)
+
+add_definitions(
+    -DVER_MAJOR=${RGB2PCT_VERSION_MAJOR}
+    -DVER_MINOR=${RGB2PCT_VERSION_MINOR}
+    -DVER_STEP=${RGB2PCT_VERSION_PATCH}
+    -DVER_TWEAK=${VERSION_SUFFIX}
+    -DAPPLICATION_NAME=${APPLICATION_NAME}
+)
+
+###############################################################################################
+# All source files needed to compile
+###############################################################################################
+set( SRCS
+    main.cpp
+    CApp.cpp
+)
+
+set( HDRS
+    version.h
+    CApp.h
+)
+
+set( UIS
+)
+
+set( RCS
+)
+
+###############################################################################################
+# Some Qt magic
+###############################################################################################
+
+qt5_wrap_ui(UI_HDRS ${UIS})
+qt5_add_resources(RC_SRCS ${RCS})
+
+###############################################################################################
+# Translation related stuff
+###############################################################################################
+translate_ts(${APPLICATION_NAME}_QM_FILES
+    UPDATE_TRANSLATIONS ${UPDATE_TRANSLATIONS}
+    UPDATE_OPTIONS "-I${CMAKE_CURRENT_SOURCE_DIR}" ${KEEP_OLD_TRANSLATIONS}
+    SOURCES ${SRCS} ${HDRS} ${UIS}
+    TEMPLATE ${APPLICATION_NAME}
+    TRANSLATION_DIR "locale"
+)
+
+###############################################################################################
+# Build source file and include paths lists
+###############################################################################################
+set(MAININP
+    ${SRCS}
+    ${HDRS}
+    ${UI_HDRS}
+    ${RC_SRCS}
+    ${${APPLICATION_NAME}_QM_FILES}
+    ${${APPLICATION_NAME}_DESKTOP_FILES}
+)
+
+include_directories(
+    SYSTEM # this prevents warnings from non-QMS headers
+    ${CMAKE_BINARY_DIR}
+    ${GDAL_INCLUDE_DIRS}
+    ${PROJ4_INCLUDE_DIRS}
+)
+
+if(APPLE)
+     INCLUDE_DIRECTORIES(/System/Library/Frameworks/Foundation.framework)
+     INCLUDE_DIRECTORIES(/System/Library/Frameworks/DiskArbitration.framework)
+endif(APPLE)
+
+
+###############################################################################################
+# Build the executable and define necessary libraries.
+###############################################################################################
+add_executable(${APPLICATION_NAME} WIN32 ${MAININP})
+
+target_link_libraries(${APPLICATION_NAME}
+    Qt5::Core
+    ${GDAL_LIBRARIES}
+    ${PROJ4_LIBRARIES}
+)
+
+if(APPLE)
+     target_link_libraries(${APPLICATION_NAME}
+     ${Foundation_LIBRARY}
+     ${DiskArbitration_LIBRARY}
+    )
+endif(APPLE)
+
+
+###############################################################################################
+# Install target related stuff
+###############################################################################################
+install(TARGETS     ${APPLICATION_NAME}                     DESTINATION     ${BIN_INSTALL_DIR})
+
+if (UNIX AND NOT WIN32 AND NOT APPLE)
+    install(FILES   ${${APPLICATION_NAME}_QM_FILES}         DESTINATION     ${DATA_INSTALL_PREFIX}/${APPLICATION_NAME}/translations)
+    install(FILES   ${${APPLICATION_NAME}_DESKTOP_FILES}    DESTINATION     ${XDG_APPS_DIR})
+endif (UNIX AND NOT WIN32 AND NOT APPLE)
diff --git a/src/qmt_rgb2pct/README.md b/src/qmt_rgb2pct/README.md
new file mode 100644
index 00000000..21e6c750
--- /dev/null
+++ b/src/qmt_rgb2pct/README.md
@@ -0,0 +1,5 @@
+This is a sub-project of QMapShack and it's not supposed to compile on it's own. Please refere to
+
+https://bitbucket.org/maproom/qmapshack/overview
+
+to check out and compile this project.
\ No newline at end of file
diff --git a/src/qmt_rgb2pct/locale/qmt_rgb2pct.ts b/src/qmt_rgb2pct/locale/qmt_rgb2pct.ts
new file mode 100644
index 00000000..02904577
--- /dev/null
+++ b/src/qmt_rgb2pct/locale/qmt_rgb2pct.ts
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+    <name>CApp</name>
+    <message>
+        <location filename="../CApp.cpp" line="63"/>
+        <source>Failed to open source file.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="68"/>
+        <source>Raster band count of source file must be either 3 or 4.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="101"/>
+        <source>Calculate optimal color table from source file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="115"/>
+        <source>Failed to create color table.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="123"/>
+        <source>Failed to open file with palette.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="131"/>
+        <source>Palette file does not have a single band with a color table</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="140"/>
+        <source>The color table must not contain a &quot;no data&quot; value and it&apos;s size must not exceed 255 colors.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="167"/>
+        <source>Save color table to: %1</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="203"/>
+        <source>Failed to create target file.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="214"/>
+        <source>Dither source file to target file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="225"/>
+        <source>Failed to dither file.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="240"/>
+        <source>Apply alpha channel as no data value to target file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="247"/>
+        <source>Failed to read from alpha channel.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="253"/>
+        <source>Failed to read from target file.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="267"/>
+        <source>Failed to write to target file.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>main</name>
+    <message>
+        <location filename="../main.cpp" line="96"/>
+        <source>
+Convert a map file with RGBA color coding to a color palette coding.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="99"/>
+        <source>Source file.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="100"/>
+        <source>Target file.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="104"/>
+        <source>Number of colors. (default: 255)</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="107"/>
+        <source>Input palette file for color table (*.vrt)</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="110"/>
+        <source>Save color table to palette file (*.vrt)</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="120"/>
+        <source>There must be a source and destination file.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="143"/>
+        <source>--ncolors must be an integer value less than 256</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+</TS>
diff --git a/src/qmt_rgb2pct/locale/qmt_rgb2pct_de.ts b/src/qmt_rgb2pct/locale/qmt_rgb2pct_de.ts
new file mode 100644
index 00000000..9622b8d9
--- /dev/null
+++ b/src/qmt_rgb2pct/locale/qmt_rgb2pct_de.ts
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de_DE">
+<context>
+    <name>CApp</name>
+    <message>
+        <location filename="../CApp.cpp" line="63"/>
+        <source>Failed to open source file.</source>
+        <translation>Konnte Quelldatei nicht öffnen.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="68"/>
+        <source>Raster band count of source file must be either 3 or 4.</source>
+        <translation>Die Anzahl der Rasterbänder muss entweder 3 oder 4 sein.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="101"/>
+        <source>Calculate optimal color table from source file</source>
+        <translation>Berechne die optimale Farbtabelle für die Quelldatei</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="115"/>
+        <source>Failed to create color table.</source>
+        <translation>Konnte die Farbtabelle nicht erstellen.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="123"/>
+        <source>Failed to open file with palette.</source>
+        <translation>Konnte die Datei mit der Palette nicht öffnen.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="131"/>
+        <source>Palette file does not have a single band with a color table</source>
+        <translation>Die Datei mit der Palette hat kein einzelnes Band mit einer Farbtabelle</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="140"/>
+        <source>The color table must not contain a &quot;no data&quot; value and it&apos;s size must not exceed 255 colors.</source>
+        <translation>Die Farbtabelle darf keinen Eintrag für &quot;no data&quot; haben und ihre Größe darf nicht 255 Farben überschreiten.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="167"/>
+        <source>Save color table to: %1</source>
+        <translation>Speichere Farbtabelle in: %1</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="203"/>
+        <source>Failed to create target file.</source>
+        <translation>Konnte Zieldatei nicht erstellen.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="214"/>
+        <source>Dither source file to target file</source>
+        <translation>Wandle Quelldatei in Zieldatei um</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="225"/>
+        <source>Failed to dither file.</source>
+        <translation>Konnte die Datei nicht umwandeln.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="240"/>
+        <source>Apply alpha channel as no data value to target file</source>
+        <translation>Wandle für die Zieldatei den Alphakanal in &quot;no data&quot; Werte um </translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="247"/>
+        <source>Failed to read from alpha channel.</source>
+        <translation>Konnte den Alphakanal nicht lesen.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="253"/>
+        <source>Failed to read from target file.</source>
+        <translation>Konnte die Zieldatei nicht lesen.</translation>
+    </message>
+    <message>
+        <location filename="../CApp.cpp" line="267"/>
+        <source>Failed to write to target file.</source>
+        <translation>Konnte die Zieldatei nicht schreiben.</translation>
+    </message>
+</context>
+<context>
+    <name>main</name>
+    <message>
+        <location filename="../main.cpp" line="96"/>
+        <source>
+Convert a map file with RGBA color coding to a color palette coding.</source>
+        <translation>
+Konvertiert eine Kartendatei mit RGBA Farbschema in eine Kartendatei mit Farbtabelle.</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="99"/>
+        <source>Source file.</source>
+        <translation>Quelldatei.</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="100"/>
+        <source>Target file.</source>
+        <translation>Zieldatei.</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="104"/>
+        <source>Number of colors. (default: 255)</source>
+        <translation>Anzahl an Farben (Vorgabe: 255)</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="107"/>
+        <source>Input palette file for color table (*.vrt)</source>
+        <translation>Datei mit Palette als Vorgabe für die Farbtabelle (*.vrt) </translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="110"/>
+        <source>Save color table to palette file (*.vrt)</source>
+        <translation>Farbtabelle in Datei mit Palette sichern (*.vrt)</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="120"/>
+        <source>There must be a source and destination file.</source>
+        <translation>Es muss eine Quell- und eine Zieldatei angegeben werden.</translation>
+    </message>
+    <message>
+        <location filename="../main.cpp" line="143"/>
+        <source>--ncolors must be an integer value less than 256</source>
+        <translation>--ncolors muss eine ganze Zahl kleiner 256 sein</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/qmt_rgb2pct/main.cpp b/src/qmt_rgb2pct/main.cpp
new file mode 100644
index 00000000..4ff9a9ae
--- /dev/null
+++ b/src/qmt_rgb2pct/main.cpp
@@ -0,0 +1,155 @@
+/**********************************************************************************************
+    Copyright (C) 2018 Oliver Eichler oliver.eichler@gmx.de
+
+    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 "CApp.h"
+#include "version.h"
+#include <QtCore>
+
+#ifdef Q_OS_MACOS
+static QDir getApplicationDir(QString subdir)
+{
+    QDir appDir(QCoreApplication::applicationDirPath());
+    appDir.cdUp();
+    appDir.cd(subdir);
+    return appDir;
+}
+#endif
+static void prepareTranslator(QString translationPath, QString translationPrefix)
+{
+    QString locale = QLocale::system().name();
+    QDir dir(translationPath);
+    if(!QFile::exists(dir.absoluteFilePath(translationPrefix + locale)))
+    {
+        locale = locale.left(2);
+    }
+
+    QCoreApplication* app =  (QCoreApplication*) QCoreApplication::instance();
+    QTranslator *qtTranslator = new QTranslator(app);
+
+    if (qtTranslator->load(translationPrefix + locale, translationPath))
+    {
+        app->installTranslator(qtTranslator);
+    }
+}
+
+static void loadTranslations()
+{
+#if defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(__FreeBSD_kernel__) || defined(__GNU__) || defined(Q_OS_CYGWIN)
+    QString resourceDir     = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+    QString translationPath = QCoreApplication::applicationDirPath();
+    translationPath.replace(QRegExp("bin$"), "share/" APP_STR "/translations");
+    prepareTranslator(resourceDir, "qt_");
+    prepareTranslator(translationPath, APP_STR "_");
+#endif
+
+#ifdef Q_OS_OSX
+    // os x
+    static QString relTranslationDir = "Resources/translations"; // app
+    QString translationPath = getApplicationDir(relTranslationDir).absolutePath();
+    prepareTranslator(translationPath, "qt_");
+    prepareTranslator(translationPath, APP_STR "_");
+#endif
+
+#ifdef Q_OS_WIN
+    QString apppath = QCoreApplication::applicationDirPath();
+    apppath = apppath.replace("/", "\\");
+    QString appResourceDir = QString("%1\\translations").arg(apppath).toUtf8();
+    prepareTranslator(appResourceDir, "qtbase_");
+    prepareTranslator(appResourceDir, APP_STR "_");
+#endif
+
+}
+
+int main(int argc, char ** argv)
+{
+    QCoreApplication app(argc, argv);
+    QCoreApplication::setApplicationName(APP_STR);
+    QCoreApplication::setApplicationVersion(VER_STR);
+    if(QString(VER_SUFFIX).isEmpty())
+    {
+        QCoreApplication::setApplicationVersion(VER_STR);
+    }
+    else
+    {
+        QCoreApplication::setApplicationVersion(VER_STR "." VER_SUFFIX);
+    }
+
+
+    loadTranslations();
+
+    QCommandLineParser parser;
+    parser.setApplicationDescription(QCoreApplication::translate("main", "\nConvert a map file with RGBA color coding to a color palette coding."));
+    parser.addHelpOption();
+    parser.addVersionOption();
+    parser.addPositionalArgument("source", QCoreApplication::translate("main", "Source file."));
+    parser.addPositionalArgument("target", QCoreApplication::translate("main", "Target file."));
+
+    parser.addOptions({
+        {
+            {"n","ncolors"}, QCoreApplication::translate("main", "Number of colors. (default: 255)"), "number", "255"
+        },
+        {
+            {"p","pct"},  QCoreApplication::translate("main", "Input palette file for color table (*.vrt)"), "filename", ""
+        },
+        {
+            {"s","sct"},  QCoreApplication::translate("main", "Save color table to palette file (*.vrt)"), "filename", ""
+        },
+    });
+
+    // Process the actual command line arguments given by the user
+    parser.process(app);
+
+    if(parser.positionalArguments().count() == 1 && parser.value("sct").isEmpty())
+    {
+        printStderrQString("");
+        printStderrQString(QCoreApplication::translate("main","There must be a source and destination file."));
+        printStderrQString("");
+        parser.showHelp(-1);
+    }
+
+    if(parser.positionalArguments().isEmpty())
+    {
+        parser.showHelp(-1);
+    }
+
+    QString srcFilename = parser.positionalArguments()[0];
+    QString tarFilename;
+    if(parser.positionalArguments().count() > 1)
+    {
+        tarFilename = parser.positionalArguments()[1];
+    }
+
+
+    bool ok = false;
+    const qint32 ncolors = parser.value("ncolors").toInt(&ok);
+    if(!ok || ncolors > 255)
+    {
+        printStderrQString("");
+        printStderrQString(QCoreApplication::translate("main","--ncolors must be an integer value less than 256"));
+        printStderrQString("");
+        parser.showHelp(-1);
+    }
+
+    QString pctFilename = parser.value("pct");
+    QString sctFilename = parser.value("sct");
+
+    CApp theApp(ncolors, pctFilename, sctFilename, srcFilename, tarFilename);
+    return theApp.exec();
+}
+
+
diff --git a/src/qmt_rgb2pct/version.h b/src/qmt_rgb2pct/version.h
new file mode 100644
index 00000000..60f94f71
--- /dev/null
+++ b/src/qmt_rgb2pct/version.h
@@ -0,0 +1,33 @@
+/**********************************************************************************************
+    Copyright (C) 2017 Oliver Eichler oliver.eichler@gmx.de
+
+    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 VERSION_H
+#define VERSION_H
+
+#ifndef _MKSTR_1
+#define _MKSTR_1(x)    #x
+#define _MKSTR(x)      _MKSTR_1(x)
+#endif
+
+#define VER_STR       _MKSTR(VER_MAJOR) "." _MKSTR (VER_MINOR) "." _MKSTR (VER_STEP)
+#define VER_SUFFIX    _MKSTR(VER_TWEAK)
+#define APP_STR       _MKSTR(APPLICATION_NAME)
+#define WHAT_STR      _MKSTR(APPLICATION_NAME) ", Version " VER_STR
+
+#endif //VERSION_H
+