1 /**********************************************************************************************
2 Copyright (C) 2018 Oliver Eichler <oliver.eichler@gmx.de>
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 **********************************************************************************************/
18
19 #include "CApp.h"
20
21 #include <gdal_alg.h>
22 #include <gdal_priv.h>
23 #include <iostream>
24
25 const GDALColorEntry CApp::noColor = {255, 255, 255, 0};
26
printStdoutQString(const QString & str)27 void printStdoutQString(const QString& str)
28 {
29 QByteArray array = str.toUtf8();
30 printf("%s", array.data());
31 printf("\n");
32 }
33
printStderrQString(const QString & str)34 void printStderrQString(const QString& str)
35 {
36 QByteArray array = str.toUtf8();
37 fprintf(stderr, "%s", array.data());
38 fprintf(stderr, "\n");
39 }
40
41
42
CApp(qint32 ncolors,const QString & pctFilename,const QString & sctFilename,const QString & srcFilename,const QString & tarFilename)43 CApp::CApp(qint32 ncolors, const QString& pctFilename, const QString& sctFilename, const QString& srcFilename, const QString& tarFilename)
44 : ncolors(ncolors)
45 , pctFilename(pctFilename)
46 , sctFilename(sctFilename)
47 , srcFilename(srcFilename)
48 , tarFilename(tarFilename)
49 {
50 GDALAllRegister();
51 }
52
exec()53 qint32 CApp::exec()
54 {
55 qint32 res = 0;
56 GDALColorTable* ct = nullptr;
57 GDALDataset* dsSrc = nullptr;
58 try
59 {
60 dsSrc = (GDALDataset*)GDALOpenShared(srcFilename.toUtf8(), GA_ReadOnly);
61 if(dsSrc == nullptr)
62 {
63 throw tr("Failed to open source file.");
64 }
65
66 if(dsSrc->GetRasterCount() < 3 || dsSrc->GetRasterCount() > 4)
67 {
68 throw tr("Raster band count of source file must be either 3 or 4.");
69 }
70
71 if(QFile(tarFilename).exists())
72 {
73 QFile::remove(tarFilename);
74 }
75
76 ct = createColorTable(ncolors, pctFilename, dsSrc);
77 saveColorTable(ct, sctFilename);
78 ditherMap(dsSrc, tarFilename, ct);
79 }
80 catch(const QString& msg)
81 {
82 printStderrQString(msg);
83 res = -1;
84 }
85
86
87 GDALClose(dsSrc);
88 delete ct;
89 return res;
90 }
91
createColorTable(qint32 ncolors,const QString & pctFilename,GDALDataset * dataset)92 GDALColorTable* CApp::createColorTable(qint32 ncolors, const QString& pctFilename, GDALDataset* dataset)
93 {
94 GDALColorTable* ct = nullptr;
95 try
96 {
97 if(pctFilename.isEmpty())
98 {
99 ct = (GDALColorTable*)GDALCreateColorTable(GPI_RGB);
100
101 printStdoutQString(tr("Calculate optimal color table from source file"));
102
103 int ok = GDALComputeMedianCutPCT(dataset->GetRasterBand(1),
104 dataset->GetRasterBand(2),
105 dataset->GetRasterBand(3),
106 nullptr,
107 ncolors,
108 ct,
109 GDALTermProgress,
110 0
111 );
112
113 if(ok != CE_None)
114 {
115 throw tr("Failed to create color table.");
116 }
117 }
118 else
119 {
120 GDALDataset* dsPct = (GDALDataset*)GDALOpenShared(pctFilename.toUtf8(), GA_ReadOnly);
121 if(dsPct == nullptr)
122 {
123 throw tr("Failed to open file with palette.");
124 }
125
126 GDALRasterBand* band = (GDALRasterBand*)dsPct->GetRasterBand(1);
127
128 if((dsPct->GetRasterCount() != 1) || (band->GetColorInterpretation() != GCI_PaletteIndex))
129 {
130 GDALClose(dsPct);
131 throw tr("Palette file does not have a single band with a color table");
132 }
133
134 int ok = 0;
135 band->GetNoDataValue(&ok);
136
137 if(ok || band->GetColorTable()->GetColorEntryCount() > 255)
138 {
139 GDALClose(dsPct);
140 throw tr("The color table must not contain a \"no data\" value and it's size must not exceed 255 colors.");
141 }
142
143 ct = dsPct->GetRasterBand(1)->GetColorTable()->Clone();
144 }
145 }
146 catch(const QString& msg)
147 {
148 delete ct;
149 throw msg;
150 }
151 return ct;
152 }
153
saveColorTable(GDALColorTable * ct,QString & sctFilename)154 void CApp::saveColorTable(GDALColorTable* ct, QString& sctFilename)
155 {
156 if(sctFilename.isEmpty())
157 {
158 return;
159 }
160
161 if(!sctFilename.endsWith(".vrt"))
162 {
163 sctFilename += ".vrt";
164 }
165
166 QByteArray buf = sctFilename.toUtf8();
167 printStdoutQString(tr("Save color table to: %1").arg(buf.data()));
168
169 GDALDriverManager* drvman = GetGDALDriverManager();
170 GDALDriver* driver = drvman->GetDriverByName("VRT");
171 GDALDataset* dataset = driver->Create(sctFilename.toUtf8(), 1, 1, 1, GDT_Byte, {});
172
173 dataset->GetRasterBand(1)->SetColorInterpretation(GCI_PaletteIndex);
174 dataset->GetRasterBand(1)->SetColorTable(ct);
175
176 dataset->FlushCache();
177 GDALClose(dataset);
178 }
179
ditherMap(GDALDataset * dsSrc,const QString & tarFilename,GDALColorTable * ct)180 void CApp::ditherMap(GDALDataset* dsSrc, const QString& tarFilename, GDALColorTable* ct)
181 {
182 if(tarFilename.isEmpty())
183 {
184 return;
185 }
186
187 qint32 xsize = dsSrc->GetRasterBand(1)->GetXSize();
188 qint32 ysize = dsSrc->GetRasterBand(1)->GetYSize();
189
190 GDALDriverManager* drvman = nullptr;
191 GDALDriver* driver = nullptr;
192 GDALDataset* dataset = nullptr;
193
194 try
195 {
196 const char* cargs[] = {"TILED=YES", "COMPRESS=LZW", 0};
197 drvman = GetGDALDriverManager();
198 driver = drvman->GetDriverByName("GTiff");
199 dataset = driver->Create(tarFilename.toUtf8(), xsize, ysize, 1, GDT_Byte, (char**)cargs);
200
201 if(dataset == nullptr)
202 {
203 throw tr("Failed to create target file.");
204 }
205
206 dataset->GetRasterBand(1)->SetColorTable(ct);
207 dataset->GetRasterBand(1)->SetNoDataValue(ct->GetColorEntryCount());
208 dataset->SetProjection(dsSrc->GetProjectionRef());
209
210 double adfGeoTransform[6] = {0};
211 dsSrc->GetGeoTransform(adfGeoTransform);
212 dataset->SetGeoTransform(adfGeoTransform);
213
214 printStdoutQString(tr("Dither source file to target file"));
215 int res = GDALDitherRGB2PCT(dsSrc->GetRasterBand(1),
216 dsSrc->GetRasterBand(2),
217 dsSrc->GetRasterBand(3),
218 dataset->GetRasterBand(1),
219 ct,
220 GDALTermProgress,
221 0
222 );
223 if(res != CE_None)
224 {
225 throw tr("Failed to dither file.");
226 }
227
228 if(dsSrc->GetRasterCount() == 3)
229 {
230 return;
231 }
232
233 GDALRasterBand* alpha = dsSrc->GetRasterBand(4);
234 GDALRasterBand* band = dataset->GetRasterBand(1);
235
236 QByteArray buffer1(xsize, 0);
237 QByteArray buffer2(xsize, 0);
238
239 quint8 nodata = band->GetNoDataValue();
240 printStdoutQString(tr("Apply alpha channel as no data value to target file"));
241 for(int y = 0; y < ysize; y++)
242 {
243 GDALTermProgress(double(xsize * y) / (xsize * ysize), 0, 0);
244 res = alpha->RasterIO(GF_Read, 0, y, xsize, 1, buffer1.data(), xsize, 1, GDT_Byte, 0, 0);
245 if(res != CE_None)
246 {
247 throw tr("Failed to read from alpha channel.");
248 }
249
250 res = band->RasterIO(GF_Read, 0, y, xsize, 1, buffer2.data(), xsize, 1, GDT_Byte, 0, 0);
251 if(res != CE_None)
252 {
253 throw tr("Failed to read from target file.");
254 }
255
256 for(int x = 0; x < xsize; x++)
257 {
258 if(buffer1[x] != char(0xFF))
259 {
260 buffer2[x] = nodata;
261 }
262 }
263
264 res = band->RasterIO(GF_Write, 0, y, xsize, 1, buffer2.data(), xsize, 1, GDT_Byte, 0, 0);
265 if(res != CE_None)
266 {
267 throw tr("Failed to write to target file.");
268 }
269 }
270 GDALTermProgress(1.0, 0, 0);
271 }
272 catch(const QString& msg)
273 {
274 GDALClose(dataset);
275 throw msg;
276 }
277
278 dataset->FlushCache();
279 GDALClose(dataset);
280 }
281