3#include "Foundation/Logging/Logger.h"
10 using namespace Cogs::Core::TerrainProvider;
16 static tmsize_t readProc(thandle_t clientData,
void* ptr, tmsize_t bytes)
19 auto byteCount = std::min(
size_t(bytes), self->size - self->offset);
20 std::memcpy(ptr, (uint8_t*)(self->ptr) + self->offset, byteCount);
21 self->offset += byteCount;
22 return tmsize_t(byteCount);
25 static tmsize_t writeProc(thandle_t,
void*, tmsize_t)
27 assert(
false &&
"Write should not be invoked");
31 static toff_t seekProc(thandle_t clientData, toff_t off,
int whence) {
35 self->offset = std::min<toff_t>(self->size, off);
38 self->offset = std::min<toff_t>(self->size, self->offset + off);
41 self->offset = self->size;
44 assert(
false &&
"Invalid whence");
50 static toff_t sizeProc(thandle_t clientData) {
55 static int closeProc(thandle_t) {
return 0; }
57 static int mapFileProc(thandle_t clientData,
void** base, toff_t* size)
60 *base = (
void*)self->ptr;
65 static void unmapFileProc(thandle_t ,
void* , toff_t ) { }
73 void accommodate(
size_t high)
75 while (out.size() < high) {
76 out.resize(out.size() + 1024 * 1024);
80 static tmsize_t writeProc(thandle_t clientData,
void* ptr, tmsize_t bytes)
82 auto* self =
reinterpret_cast<IOWriteWrapper*
>(clientData);
83 self->accommodate(self->offset + bytes);
84 std::memcpy(
static_cast<uint8_t*
>(self->out.data()) + self->offset, ptr, bytes);
85 self->offset += bytes;
86 if (self->size < self->offset) {
87 self->size = self->offset;
92 static toff_t sizeProc(thandle_t clientData) {
93 auto* self =
reinterpret_cast<IOWriteWrapper*
>(clientData);
97 static int closeProc(thandle_t clientData) {
98 auto* self =
reinterpret_cast<IOWriteWrapper*
>(clientData);
99 self->out.resize(self->size);
103 static tmsize_t readProc(thandle_t ,
void* , tmsize_t )
109 static toff_t seekProc(thandle_t clientData, toff_t off,
int whence) {
110 auto* self =
reinterpret_cast<IOWriteWrapper*
>(clientData);
119 self->offset = self->offset + off;
122 self->offset = self->size;
125 assert(
false &&
"Invalid whence");
128 if (self->size < self->offset) {
129 self->size = self->offset;
130 self->accommodate(self->size);
139#define TIFFTAG_GDAL_METADATA 42112
140#define TIFFTAG_GDAL_NODATA 42113
141#define TIFFTAG_MODELPIXELSCALE 33550
142#define TIFFTAG_MODELTIEPOINT 33922
143#define TIFFTAG_MODELTRANSFORMATION 34264
144#define TIFFTAG_GEOKEYDIRECTORY 34735
146#define TIFFTAG_INTERGRAPH_MATRIX 33920
147#define TIFFTAG_JPL_CARTO_IFD 34263
148#define TIFFTAG_GEODOUBLEPARAMS 34736
149#define TIFFTAG_GEOASCIIPARAMS 34737
151 static const TIFFFieldInfo extraFieldInfo[] = {
152 { TIFFTAG_GDAL_METADATA, -1, -1, TIFF_ASCII, FIELD_CUSTOM, 1, 0,
const_cast<char*
>(
"GDALMetaData") },
153 { TIFFTAG_GDAL_NODATA, -1, -1, TIFF_ASCII, FIELD_CUSTOM, 1, 0,
const_cast<char*
>(
"GDALNoData") },
154 { TIFFTAG_MODELTIEPOINT, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, 1, 1,
const_cast<char*
>(
"ModelTiepointTag") },
155 { TIFFTAG_MODELPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, 1, 1,
const_cast<char*
>(
"ModelPixelScaleTag") },
156 { TIFFTAG_MODELTRANSFORMATION, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, 1, 1,
const_cast<char*
>(
"ModelTransformation") },
157 { TIFFTAG_GEOKEYDIRECTORY, -1, -1, TIFF_SHORT, FIELD_CUSTOM, 1, 1,
const_cast<char*
>(
"GeoKeyDirectory") },
158 { TIFFTAG_INTERGRAPH_MATRIX, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, 1, 1,
const_cast<char*
>(
"Intergraph TransformationMatrix") },
159 { TIFFTAG_GEODOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, 1, 1,
const_cast<char*
>(
"GeoDoubleParams") },
160 { TIFFTAG_GEOASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, 1, 0,
const_cast<char*
>(
"GeoASCIIParams") },
161 { TIFFTAG_JPL_CARTO_IFD, 1, 1, TIFF_LONG, FIELD_CUSTOM, 1, 1,
const_cast<char*
>(
"JPL Carto IFD offset") },
164 static TIFFExtendProc previousExtender;
166 static void defaultDirectory(TIFF* tif)
168 TIFFMergeFieldInfo(tif, extraFieldInfo,
sizeof(extraFieldInfo) /
sizeof(extraFieldInfo[0]));
169 if (previousExtender) {
170 previousExtender(tif);
174 static struct CustomTIFFFIelds
178 LOG_DEBUG(logger,
"Registering custom TIFF fields");
179 previousExtender = TIFFSetTagExtender(defaultDirectory);
185Cogs::Core::TerrainProvider::GeoTiffView::GeoTiffView(
const void* ptr,
size_t size)
186 : ptr(ptr), size(size), offset(0)
188 tif = TIFFClientOpen(
"contents",
"r",
this,
190 seekProc, closeProc, sizeProc,
191 mapFileProc, unmapFileProc);
192 if (tif ==
nullptr) {
193 LOG_ERROR(logger,
"Failed to open TIFF");
198 uint16_t* tmp_u16p =
nullptr;
199 uint32_t tmp_u32 = 0;
200 uint16_t tmp_u16 = 0;
201 double tmp_f64 = 0.0;
202 double* tmp_f64p =
nullptr;
203 const char* tmp_str =
nullptr;
206 if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &tmp_u32)) { LOG_ERROR(logger,
"missing TIFFTAG_IMAGEWIDTH");
return; }
else { width = tmp_u32; }
207 if (!TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &tmp_u32)) { LOG_ERROR(logger,
"missing TIFFTAG_IMAGELENGTH");
return; }
else { height = tmp_u32; }
213 if (!TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps)) { LOG_ERROR(logger,
"missing TIFFTAG_BITSPERSAMPLE");
return; }
214 if (!TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ssp)) { LOG_ERROR(logger,
"missing TIFFTAG_SAMPLESPERPIXEL");
return; }
215 if (!TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &fmt)) { LOG_ERROR(logger,
"missing TIFFTAG_SAMPLEFORMAT");
return; }
216 if ((bps == 32) && (ssp == 1) && (fmt == SAMPLEFORMAT_IEEEFP)) {
217 format = Cogs::TextureFormat::R32_FLOAT;
220 LOG_ERROR(logger,
"Unsupported pixel format bits_per_sample=%u, samples_per_pixel=%u, sample_format=%u",
unsigned(bps),
unsigned(ssp),
unsigned(fmt));
225 if (TIFFGetField(tif, TIFFTAG_SMINSAMPLEVALUE, &tmp_f64)) { minSample = float(tmp_f64); }
226 else if (TIFFGetField(tif, TIFFTAG_MINSAMPLEVALUE, &tmp_u16)) { minSample = tmp_u16; }
227 if (TIFFGetField(tif, TIFFTAG_SMAXSAMPLEVALUE, &tmp_f64)) { maxSample = float(tmp_f64); }
228 else if (TIFFGetField(tif, TIFFTAG_MAXSAMPLEVALUE, &tmp_u16)) { maxSample = tmp_u16; }
231 if (TIFFGetField(tif, TIFFTAG_GDAL_NODATA, &tmp_str)) {
232 if (strcmp(
"nan", tmp_str) == 0) {
233 noData = std::numeric_limits<float>::quiet_NaN();
237 noData = std::strtof(tmp_str, &end);
238 if (!end || *end !=
'\0') {
239 LOG_WARNING(logger,
"Failed to parse nodata value '%s'", tmp_str);
244 if (TIFFGetField(tif, TIFFTAG_MODELPIXELSCALE, &tmp_u16, &tmp_f64p)) {
246 scale[0] = tmp_f64p[0];
247 scale[1] = tmp_f64p[1];
248 scale[2] = tmp_f64p[2];
251 LOG_WARNING(logger,
"TIFFTAG_MODELPIXELSACE has unexpected count %u, expected 3.",
unsigned(tmp_u16));
255 if (TIFFGetField(tif, TIFFTAG_MODELTIEPOINT, &tmp_u16, &tmp_f64p)) {
257 for (
size_t i = 0; i < 6; i++) { tiePoint[i] = tmp_f64p[i]; }
258 origin[0] = tiePoint[3] - (scale[0] == 0.0 ? 1.0 : scale[0]) * tiePoint[0];
259 origin[1] = tiePoint[4] - (scale[1] == 0.0 ? 1.0 : scale[1]) * tiePoint[1];
260 origin[2] = tiePoint[5] - (scale[2] == 0.0 ? 1.0 : scale[2]) * tiePoint[2];
263 LOG_WARNING(logger,
"TIFFTAG_MODELTIEPOINT has unexpected count %u, expected 6",
unsigned(tmp_u16));
267 if (TIFFGetField(tif, TIFFTAG_MODELTRANSFORMATION, &tmp_u16, &tmp_f64p)) {
269 scale[0] = tmp_f64p[0];
270 scale[1] = tmp_f64p[5];
271 scale[2] = tmp_f64p[10];
272 origin[0] = tmp_f64p[3];
273 origin[1] = tmp_f64p[7];
274 origin[2] = tmp_f64p[11];
277 LOG_WARNING(logger,
"TIFFTAG_MODELTIEPOINT has unexpected count %u, expected 16",
unsigned(tmp_u16));
281 if (TIFFGetField(tif, TIFFTAG_GDAL_METADATA, &tmp_str)) {
282 LOG_DEBUG(logger,
"Unprocessed GDAL metadata: %s", tmp_str);
285 if (TIFFGetField(tif, TIFFTAG_GEOKEYDIRECTORY, &tmp_u16, &tmp_u16p)) {
286 LOG_DEBUG(logger,
"Unprocessed %u geokeys: %u, %u, %u, %u,...",
unsigned(tmp_u16),
287 unsigned(tmp_u16p[0]),
unsigned(tmp_u16p[1]),
288 unsigned(tmp_u16p[2]),
unsigned(tmp_u16p[3]));
292 LOG_DEBUG(logger,
"width=%u, height=%u, noData=%e, min=%f, max=%f, scale=[%f, %f, %f], origin=[%f, %f, %f]",
294 noData.value_or(std::numeric_limits<float>::infinity()),
295 minSample.value_or(std::numeric_limits<float>::infinity()),
296 maxSample.value_or(std::numeric_limits<float>::infinity()),
297 scale[0], scale[1], scale[2],
298 origin[0], origin[1], origin[2]);
303Cogs::Core::TerrainProvider::GeoTiffView::~GeoTiffView()
305 if (tif) { TIFFClose(tif); }
308bool Cogs::Core::TerrainProvider::GeoTiffView::getFullImage(
Memory::MemoryBuffer& output,
float suggested_nodata)
310 if (!valid)
return false;
314 case Cogs::TextureFormat::R32_FLOAT: {
315 if (TIFFIsTiled(tif)) {
319 TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth);
320 TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileHeight);
322 auto buf = _TIFFmalloc(TIFFTileSize(tif));
323 if (buf ==
nullptr)
return false;
325 size_t widthMultipleOfTile =
static_cast<size_t>(std::ceil((
float)width / tileWidth) * tileWidth);
326 size_t heightMultipleOfTile =
static_cast<size_t>(std::ceil((
float)height / tileHeight) * tileHeight);
327 output.resize(
sizeof(
float) * widthMultipleOfTile * heightMultipleOfTile);
329 float* rowStart =
reinterpret_cast<float*
>(output.data());
331 for (uint32_t y = 0; y < height; y += tileHeight, rowStart += (tileHeight * width)) {
332 float* tileStart = rowStart;
334 for (uint32_t x = 0; x < width; x += tileWidth, tileStart += tileWidth) {
335 if (TIFFReadTile(tif, buf, x, y, 0, 0) > 0) {
336 float* write = tileStart;
337 float* read =
reinterpret_cast<float*
>(buf);
339 for (uint32_t dy = tileHeight; dy--; write += width, read += tileWidth) {
340 memcpy(write, read, tileWidth *
sizeof(
float));
348 auto* buf = _TIFFmalloc(TIFFStripSize(tif));
350 if (buf ==
nullptr)
return false;
353 output.resize(
sizeof(
float) * width * height);
354 for (tstrip_t strip = 0, noOfStrips = TIFFNumberOfStrips(tif); strip < noOfStrips; strip++) {
355 auto byteCount = (size_t)TIFFReadEncodedStrip(tif, strip, buf, (tsize_t)-1);
357 std::memcpy((uint8_t*)output.data() + o, buf, byteCount);
359 assert(o <= output.size());
364 auto* oo = (
float*)output.data();
365 auto* ee = oo + size_t(width) * size_t(height);
366 float match_nodata = noData.value_or(suggested_nodata);
367 float replace_nodata = std::numeric_limits<float>::quiet_NaN();
368 float min = std::numeric_limits<float>::max();
369 float max = -std::numeric_limits<float>::max();
370 if (std::isfinite(match_nodata)) {
371 for (
auto* p = oo; p < ee; p++) {
377 if (min > v) min = v;
378 if (max < v) max = v;
383 for (
auto * p = oo; p < ee; p++) {
385 if (!std::isfinite(v)) {
389 if (min > v) min = v;
390 if (max < v) max = v;
409 IOWriteWrapper ww{ out };
411 uint16_t spp, bpp, fmt;
412 uint16_t compression = COMPRESSION_DEFLATE;
414 uint16_t photometric;
417 case Cogs::Format::R32_FLOAT:
420 fmt = SAMPLEFORMAT_IEEEFP;
421 predictor = PREDICTOR_FLOATINGPOINT;
422 photometric = PHOTOMETRIC_MINISBLACK;
424 case Cogs::Format::R8G8B8A8_UINT:
427 fmt = SAMPLEFORMAT_UINT;
428 predictor = PREDICTOR_HORIZONTAL;
429 photometric = PHOTOMETRIC_RGB;
433 LOG_ERROR(logger,
"Unsupported format %u", (
unsigned)format);
437 auto tif = TIFFClientOpen(
"contents",
"w", &ww,
438 IOWriteWrapper::readProc, IOWriteWrapper::writeProc,
439 IOWriteWrapper::seekProc, IOWriteWrapper::closeProc, IOWriteWrapper::sizeProc,
441 if (tif ==
nullptr) {
442 LOG_ERROR(logger,
"Failed to open TIFF");
446 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32_t)w);
447 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32_t)h);
448 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp);
449 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bpp);
450 TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, fmt);
451 TIFFSetField(tif, TIFFTAG_COMPRESSION, compression);
452 TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor);
453 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
454 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
455 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
456 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, w * spp));
458 auto* ptr =
static_cast<const uint8_t*
>(in.data());
459 for (
unsigned j = 0; j < h; j++) {
460 if (TIFFWriteScanline(tif, (
void*)ptr, j, 0) != 1) {
461 LOG_ERROR(logger,
"Error writing scanline");
464 ptr += w * ((bpp * spp) / 8);
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept