Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Raster Matrix Format
4 : * Purpose: Read/write raster files used in GIS "Integratsia"
5 : * (also known as "Panorama" GIS).
6 : * Author: Andrey Kiselev, dron@ak4719.spb.edu
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2005, Andrey Kiselev <dron@ak4719.spb.edu>
10 : * Copyright (c) 2007-2012, Even Rouault <even dot rouault at spatialys.com>
11 : * Copyright (c) 2023, NextGIS <info@nextgis.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 : #include <algorithm>
16 : #include <array>
17 : #include <limits>
18 :
19 : #include "cpl_string.h"
20 : #include "gdal_frmts.h"
21 : #include "ogr_spatialref.h"
22 :
23 : #include "rmfdataset.h"
24 :
25 : #include "cpl_safemaths.hpp"
26 :
27 : constexpr int RMF_DEFAULT_BLOCKXSIZE = 256;
28 : constexpr int RMF_DEFAULT_BLOCKYSIZE = 256;
29 :
30 : static const char RMF_SigRSW[] = {'R', 'S', 'W', '\0'};
31 : static const char RMF_SigRSW_BE[] = {'\0', 'W', 'S', 'R'};
32 : static const char RMF_SigMTW[] = {'M', 'T', 'W', '\0'};
33 :
34 : static const char RMF_UnitsEmpty[] = "";
35 : static const char RMF_UnitsM[] = "m";
36 : static const char RMF_UnitsCM[] = "cm";
37 : static const char RMF_UnitsDM[] = "dm";
38 : static const char RMF_UnitsMM[] = "mm";
39 :
40 : constexpr double RMF_DEFAULT_SCALE = 10000.0;
41 : constexpr double RMF_DEFAULT_RESOLUTION = 100.0;
42 :
43 : constexpr const char *MD_VERSION_KEY = "VERSION";
44 : constexpr const char *MD_NAME_KEY = "NAME";
45 : constexpr const char *MD_SCALE_KEY = "SCALE";
46 : constexpr const char *MD_FRAME_KEY = "FRAME";
47 :
48 : constexpr const char *MD_MATH_BASE_MAP_TYPE_KEY = "MATH_BASE.Map type";
49 : constexpr const char *MD_MATH_BASE_PROJECTION_KEY = "MATH_BASE.Projection";
50 :
51 : constexpr int nMaxFramePointCount = 2048;
52 : constexpr GInt32 nPolygonType =
53 : 2147385342; // 2147385342 magic number for polygon
54 :
55 : /* -------------------------------------------------------------------- */
56 : /* Note: Due to the fact that in the early versions of RMF */
57 : /* format the field of the iEPSGCode was marked as a 'reserved', */
58 : /* in the header on its place in many cases garbage values were written.*/
59 : /* Most of them can be weeded out by the minimum EPSG code value. */
60 : /* */
61 : /* see: Surveying and Positioning Guidance Note Number 7, part 1 */
62 : /* Using the EPSG Geodetic Parameter Dataset p. 22 */
63 : /* http://www.epsg.org/Portals/0/373-07-1.pdf */
64 : /* -------------------------------------------------------------------- */
65 : constexpr GInt32 RMF_EPSG_MIN_CODE = 1024;
66 :
67 74 : static char *RMFUnitTypeToStr(GUInt32 iElevationUnit)
68 : {
69 74 : switch (iElevationUnit)
70 : {
71 68 : case 0:
72 68 : return CPLStrdup(RMF_UnitsM);
73 0 : case 1:
74 0 : return CPLStrdup(RMF_UnitsDM);
75 1 : case 2:
76 1 : return CPLStrdup(RMF_UnitsCM);
77 5 : case 3:
78 5 : return CPLStrdup(RMF_UnitsMM);
79 0 : default:
80 0 : return CPLStrdup(RMF_UnitsEmpty);
81 : }
82 : }
83 :
84 80 : static GUInt32 RMFStrToUnitType(const char *pszUnit, int *pbSuccess = nullptr)
85 : {
86 80 : if (pbSuccess != nullptr)
87 : {
88 3 : *pbSuccess = TRUE;
89 : }
90 80 : if (EQUAL(pszUnit, RMF_UnitsM))
91 0 : return 0;
92 80 : else if (EQUAL(pszUnit, RMF_UnitsDM))
93 0 : return 1;
94 80 : else if (EQUAL(pszUnit, RMF_UnitsCM))
95 1 : return 2;
96 79 : else if (EQUAL(pszUnit, RMF_UnitsMM))
97 1 : return 3;
98 : else
99 : {
100 : // There is no 'invalid unit' in RMF format. So meter is default...
101 78 : if (pbSuccess != nullptr)
102 : {
103 1 : *pbSuccess = FALSE;
104 : }
105 78 : return 0;
106 : }
107 : }
108 :
109 : /************************************************************************/
110 : /* ==================================================================== */
111 : /* RMFRasterBand */
112 : /* ==================================================================== */
113 : /************************************************************************/
114 :
115 : /************************************************************************/
116 : /* RMFRasterBand() */
117 : /************************************************************************/
118 :
119 419 : RMFRasterBand::RMFRasterBand(RMFDataset *poDSIn, int nBandIn,
120 419 : GDALDataType eType)
121 838 : : nLastTileWidth(poDSIn->GetRasterXSize() % poDSIn->sHeader.nTileWidth),
122 838 : nLastTileHeight(poDSIn->GetRasterYSize() % poDSIn->sHeader.nTileHeight),
123 419 : nDataSize(GDALGetDataTypeSizeBytes(eType))
124 : {
125 419 : poDS = poDSIn;
126 419 : nBand = nBandIn;
127 :
128 419 : eDataType = eType;
129 419 : nBlockXSize = poDSIn->sHeader.nTileWidth;
130 419 : nBlockYSize = poDSIn->sHeader.nTileHeight;
131 419 : nBlockSize = nBlockXSize * nBlockYSize;
132 419 : nBlockBytes = nBlockSize * nDataSize;
133 :
134 : #ifdef DEBUG
135 419 : CPLDebug("RMF",
136 : "Band %d: tile width is %d, tile height is %d, "
137 : " last tile width %u, last tile height %u, "
138 : "bytes per pixel is %d, data type size is %d",
139 : nBand, nBlockXSize, nBlockYSize, nLastTileWidth, nLastTileHeight,
140 419 : poDSIn->sHeader.nBitDepth / 8, nDataSize);
141 : #endif
142 419 : }
143 :
144 : /************************************************************************/
145 : /* ~RMFRasterBand() */
146 : /************************************************************************/
147 :
148 838 : RMFRasterBand::~RMFRasterBand()
149 : {
150 838 : }
151 :
152 : /************************************************************************/
153 : /* IReadBlock() */
154 : /************************************************************************/
155 :
156 454 : CPLErr RMFRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
157 : {
158 454 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
159 :
160 454 : CPLAssert(poGDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
161 : pImage != nullptr);
162 :
163 454 : memset(pImage, 0, nBlockBytes);
164 :
165 454 : GUInt32 nRawXSize = nBlockXSize;
166 454 : GUInt32 nRawYSize = nBlockYSize;
167 :
168 454 : if (nLastTileWidth &&
169 381 : static_cast<GUInt32>(nBlockXOff) == poGDS->nXTiles - 1)
170 176 : nRawXSize = nLastTileWidth;
171 :
172 454 : if (nLastTileHeight &&
173 365 : static_cast<GUInt32>(nBlockYOff) == poGDS->nYTiles - 1)
174 198 : nRawYSize = nLastTileHeight;
175 :
176 454 : GUInt32 nRawBytes = nRawXSize * nRawYSize * poGDS->sHeader.nBitDepth / 8;
177 :
178 : // Direct read optimization
179 454 : if (poGDS->nBands == 1 && poGDS->sHeader.nBitDepth >= 8 &&
180 183 : nRawXSize == static_cast<GUInt32>(nBlockXSize) &&
181 71 : nRawYSize == static_cast<GUInt32>(nBlockYSize))
182 : {
183 67 : bool bNullTile = false;
184 67 : if (CE_None != poGDS->ReadTile(nBlockXOff, nBlockYOff,
185 : reinterpret_cast<GByte *>(pImage),
186 : nRawBytes, nRawXSize, nRawYSize,
187 : bNullTile))
188 : {
189 0 : CPLError(CE_Failure, CPLE_FileIO,
190 : "Failed to read tile xOff %d yOff %d", nBlockXOff,
191 : nBlockYOff);
192 0 : return CE_Failure;
193 : }
194 67 : if (bNullTile)
195 : {
196 : const int nChunkSize =
197 1 : std::max(1, GDALGetDataTypeSizeBytes(eDataType));
198 1 : const GPtrDiff_t nWords =
199 1 : static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
200 1 : GDALCopyWords64(&poGDS->sHeader.dfNoData, GDT_Float64, 0, pImage,
201 : eDataType, nChunkSize, nWords);
202 : }
203 67 : return CE_None;
204 : }
205 : #ifdef DEBUG
206 387 : CPLDebug("RMF", "IReadBlock nBand %d, RawSize [%d, %d], Bits %u", nBand,
207 : nRawXSize, nRawYSize, poGDS->sHeader.nBitDepth);
208 : #endif // DEBUG
209 387 : if (poGDS->pabyCurrentTile == nullptr ||
210 290 : poGDS->nCurrentTileXOff != nBlockXOff ||
211 102 : poGDS->nCurrentTileYOff != nBlockYOff ||
212 102 : poGDS->nCurrentTileBytes != nRawBytes)
213 : {
214 285 : if (poGDS->pabyCurrentTile == nullptr)
215 : {
216 97 : GUInt32 nMaxTileBytes = poGDS->sHeader.nTileWidth *
217 97 : poGDS->sHeader.nTileHeight *
218 97 : poGDS->sHeader.nBitDepth / 8;
219 97 : poGDS->pabyCurrentTile = reinterpret_cast<GByte *>(
220 97 : VSIMalloc(std::max(1U, nMaxTileBytes)));
221 97 : if (!poGDS->pabyCurrentTile)
222 : {
223 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
224 : "Can't allocate tile block of size %lu.\n%s",
225 : static_cast<unsigned long>(nMaxTileBytes),
226 0 : VSIStrerror(errno));
227 0 : poGDS->nCurrentTileBytes = 0;
228 0 : return CE_Failure;
229 : }
230 : }
231 :
232 285 : poGDS->nCurrentTileXOff = nBlockXOff;
233 285 : poGDS->nCurrentTileYOff = nBlockYOff;
234 285 : poGDS->nCurrentTileBytes = nRawBytes;
235 :
236 285 : if (CE_None != poGDS->ReadTile(nBlockXOff, nBlockYOff,
237 : poGDS->pabyCurrentTile, nRawBytes,
238 : nRawXSize, nRawYSize,
239 285 : poGDS->bCurrentTileIsNull))
240 : {
241 0 : CPLError(CE_Failure, CPLE_FileIO,
242 : "Failed to read tile xOff %d yOff %d", nBlockXOff,
243 : nBlockYOff);
244 0 : poGDS->nCurrentTileBytes = 0;
245 0 : return CE_Failure;
246 : }
247 : }
248 :
249 : /* -------------------------------------------------------------------- */
250 : /* Deinterleave pixels from input buffer. */
251 : /* -------------------------------------------------------------------- */
252 :
253 387 : if (poGDS->bCurrentTileIsNull)
254 : {
255 0 : const int nChunkSize = std::max(1, GDALGetDataTypeSizeBytes(eDataType));
256 0 : const GPtrDiff_t nWords =
257 0 : static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize;
258 0 : GDALCopyWords64(&poGDS->sHeader.dfNoData, GDT_Float64, 0, pImage,
259 : eDataType, nChunkSize, nWords);
260 0 : return CE_None;
261 : }
262 387 : else if ((poGDS->eRMFType == RMFT_RSW &&
263 283 : (poGDS->sHeader.nBitDepth == 8 ||
264 271 : poGDS->sHeader.nBitDepth == 24 ||
265 9 : poGDS->sHeader.nBitDepth == 32)) ||
266 107 : (poGDS->eRMFType == RMFT_MTW))
267 : {
268 384 : const size_t nTilePixelSize = poGDS->sHeader.nBitDepth / 8;
269 384 : const size_t nTileLineSize = nTilePixelSize * nRawXSize;
270 384 : const size_t nBlockLineSize =
271 384 : static_cast<size_t>(nDataSize) * nBlockXSize;
272 384 : int iDstBand = (poGDS->nBands - nBand);
273 50423 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
274 : {
275 : GByte *pabySrc;
276 : GByte *pabyDst;
277 50039 : pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize +
278 50039 : iDstBand * nDataSize;
279 50039 : pabyDst =
280 50039 : reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
281 50039 : GDALCopyWords(pabySrc, eDataType, static_cast<int>(nTilePixelSize),
282 50039 : pabyDst, eDataType, static_cast<int>(nDataSize),
283 : nRawXSize);
284 : }
285 384 : return CE_None;
286 : }
287 3 : else if (poGDS->eRMFType == RMFT_RSW && poGDS->sHeader.nBitDepth == 16 &&
288 0 : poGDS->nBands == 3)
289 : {
290 0 : const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
291 0 : const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
292 0 : const size_t nBlockLineSize =
293 0 : static_cast<size_t>(nDataSize) * nBlockXSize;
294 :
295 0 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
296 : {
297 : GUInt16 *pabySrc;
298 : GByte *pabyDst;
299 0 : pabySrc = reinterpret_cast<GUInt16 *>(poGDS->pabyCurrentTile +
300 0 : iLine * nTileLineSize);
301 0 : pabyDst =
302 0 : reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
303 :
304 0 : for (GUInt32 i = 0; i < nRawXSize; i++)
305 : {
306 0 : switch (nBand)
307 : {
308 0 : case 1:
309 0 : pabyDst[i] =
310 0 : static_cast<GByte>((pabySrc[i] & 0x7c00) >> 7);
311 0 : break;
312 0 : case 2:
313 0 : pabyDst[i] =
314 0 : static_cast<GByte>((pabySrc[i] & 0x03e0) >> 2);
315 0 : break;
316 0 : case 3:
317 0 : pabyDst[i] =
318 0 : static_cast<GByte>((pabySrc[i] & 0x1F) << 3);
319 0 : break;
320 0 : default:
321 0 : break;
322 : }
323 : }
324 : }
325 0 : return CE_None;
326 : }
327 3 : else if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 &&
328 3 : poGDS->sHeader.nBitDepth == 4)
329 : {
330 2 : if (poGDS->nCurrentTileBytes != (nBlockSize + 1) / 2)
331 : {
332 0 : CPLError(CE_Failure, CPLE_AppDefined,
333 : "Tile has %d bytes, %d were expected",
334 0 : poGDS->nCurrentTileBytes, (nBlockSize + 1) / 2);
335 0 : return CE_Failure;
336 : }
337 :
338 2 : const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
339 2 : const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
340 2 : const size_t nBlockLineSize =
341 2 : static_cast<size_t>(nDataSize) * nBlockXSize;
342 :
343 464 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
344 : {
345 : GByte *pabySrc;
346 : GByte *pabyDst;
347 462 : pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize;
348 462 : pabyDst =
349 462 : reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
350 112266 : for (GUInt32 i = 0; i < nRawXSize; ++i)
351 : {
352 111804 : if (i & 0x01)
353 55902 : pabyDst[i] = (*pabySrc++ & 0xF0) >> 4;
354 : else
355 55902 : pabyDst[i] = *pabySrc & 0x0F;
356 : }
357 : }
358 2 : return CE_None;
359 : }
360 1 : else if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1 &&
361 1 : poGDS->sHeader.nBitDepth == 1)
362 : {
363 1 : if (poGDS->nCurrentTileBytes != (nBlockSize + 7) / 8)
364 : {
365 0 : CPLError(CE_Failure, CPLE_AppDefined,
366 : "Tile has %d bytes, %d were expected",
367 0 : poGDS->nCurrentTileBytes, (nBlockSize + 7) / 8);
368 0 : return CE_Failure;
369 : }
370 :
371 1 : const size_t nTilePixelBits = poGDS->sHeader.nBitDepth;
372 1 : const size_t nTileLineSize = nTilePixelBits * nRawXSize / 8;
373 1 : const size_t nBlockLineSize =
374 1 : static_cast<size_t>(nDataSize) * nBlockXSize;
375 :
376 232 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
377 : {
378 : GByte *pabySrc;
379 : GByte *pabyDst;
380 231 : pabySrc = poGDS->pabyCurrentTile + iLine * nTileLineSize;
381 231 : pabyDst =
382 231 : reinterpret_cast<GByte *>(pImage) + iLine * nBlockLineSize;
383 :
384 57519 : for (GUInt32 i = 0; i < nRawXSize; ++i)
385 : {
386 57288 : switch (i & 0x7)
387 : {
388 7161 : case 0:
389 7161 : pabyDst[i] = (*pabySrc & 0x80) >> 7;
390 7161 : break;
391 7161 : case 1:
392 7161 : pabyDst[i] = (*pabySrc & 0x40) >> 6;
393 7161 : break;
394 7161 : case 2:
395 7161 : pabyDst[i] = (*pabySrc & 0x20) >> 5;
396 7161 : break;
397 7161 : case 3:
398 7161 : pabyDst[i] = (*pabySrc & 0x10) >> 4;
399 7161 : break;
400 7161 : case 4:
401 7161 : pabyDst[i] = (*pabySrc & 0x08) >> 3;
402 7161 : break;
403 7161 : case 5:
404 7161 : pabyDst[i] = (*pabySrc & 0x04) >> 2;
405 7161 : break;
406 7161 : case 6:
407 7161 : pabyDst[i] = (*pabySrc & 0x02) >> 1;
408 7161 : break;
409 7161 : case 7:
410 7161 : pabyDst[i] = *pabySrc++ & 0x01;
411 7161 : break;
412 0 : default:
413 0 : break;
414 : }
415 : }
416 : }
417 1 : return CE_None;
418 : }
419 :
420 0 : CPLError(CE_Failure, CPLE_AppDefined,
421 : "Invalid block data type. BitDepth %d, nBands %d",
422 0 : static_cast<int>(poGDS->sHeader.nBitDepth), poGDS->nBands);
423 :
424 0 : return CE_Failure;
425 : }
426 :
427 : /************************************************************************/
428 : /* IWriteBlock() */
429 : /************************************************************************/
430 :
431 454 : CPLErr RMFRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage)
432 : {
433 454 : CPLAssert(poDS != nullptr && nBlockXOff >= 0 && nBlockYOff >= 0 &&
434 : pImage != nullptr);
435 :
436 454 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
437 :
438 : // First drop current tile read by IReadBlock
439 454 : poGDS->nCurrentTileBytes = 0;
440 :
441 454 : GUInt32 nRawXSize = nBlockXSize;
442 454 : GUInt32 nRawYSize = nBlockYSize;
443 :
444 454 : if (nLastTileWidth &&
445 426 : static_cast<GUInt32>(nBlockXOff) == poGDS->nXTiles - 1)
446 103 : nRawXSize = nLastTileWidth;
447 :
448 454 : if (nLastTileHeight &&
449 402 : static_cast<GUInt32>(nBlockYOff) == poGDS->nYTiles - 1)
450 136 : nRawYSize = nLastTileHeight;
451 :
452 454 : const size_t nTilePixelSize =
453 454 : static_cast<size_t>(nDataSize) * poGDS->nBands;
454 454 : const size_t nTileLineSize = nTilePixelSize * nRawXSize;
455 454 : const size_t nTileSize = nTileLineSize * nRawYSize;
456 454 : const size_t nBlockLineSize = static_cast<size_t>(nDataSize) * nBlockXSize;
457 :
458 : #ifdef DEBUG
459 454 : CPLDebug(
460 : "RMF",
461 : "IWriteBlock BlockSize [%d, %d], RawSize [%d, %d], size %d, nBand %d",
462 : nBlockXSize, nBlockYSize, nRawXSize, nRawYSize,
463 : static_cast<int>(nTileSize), nBand);
464 : #endif // DEBUG
465 :
466 454 : if (poGDS->nBands == 1 && nRawXSize == static_cast<GUInt32>(nBlockXSize) &&
467 24 : nRawYSize == static_cast<GUInt32>(nBlockYSize))
468 : { // Immediate write
469 46 : return poGDS->WriteTile(
470 : nBlockXOff, nBlockYOff, reinterpret_cast<GByte *>(pImage),
471 23 : static_cast<size_t>(nRawXSize) * nRawYSize * nDataSize, nRawXSize,
472 23 : nRawYSize);
473 : }
474 : else
475 : { // Try to construct full tile in memory and write later
476 431 : const GUInt32 nTile = nBlockYOff * poGDS->nXTiles + nBlockXOff;
477 :
478 : // Find tile
479 431 : auto poTile(poGDS->oUnfinishedTiles.find(nTile));
480 431 : if (poTile == poGDS->oUnfinishedTiles.end())
481 : {
482 167 : RMFTileData oTile;
483 167 : oTile.oData.resize(nTileSize);
484 : // If not found, but exist on disk than read it
485 167 : if (poGDS->paiTiles[2 * nTile + 1])
486 : {
487 : CPLErr eRes;
488 0 : bool bNullTile = false;
489 : eRes =
490 0 : poGDS->ReadTile(nBlockXOff, nBlockYOff, oTile.oData.data(),
491 : nTileSize, nRawXSize, nRawYSize, bNullTile);
492 0 : if (eRes != CE_None)
493 : {
494 0 : CPLError(CE_Failure, CPLE_FileIO,
495 : "Can't read block with offset [%d, %d]",
496 : nBlockXOff, nBlockYOff);
497 0 : return eRes;
498 : }
499 : }
500 : poTile = poGDS->oUnfinishedTiles.insert(
501 167 : poGDS->oUnfinishedTiles.end(), std::make_pair(nTile, oTile));
502 : }
503 :
504 431 : GByte *pabyTileData = poTile->second.oData.data();
505 :
506 : // Copy new data to a tile
507 431 : int iDstBand = (poGDS->nBands - nBand);
508 79381 : for (GUInt32 iLine = 0; iLine != nRawYSize; ++iLine)
509 : {
510 : const GByte *pabySrc;
511 : GByte *pabyDst;
512 78950 : pabySrc = reinterpret_cast<const GByte *>(pImage) +
513 78950 : iLine * nBlockLineSize;
514 78950 : pabyDst =
515 78950 : pabyTileData + iLine * nTileLineSize + iDstBand * nDataSize;
516 78950 : GDALCopyWords(pabySrc, eDataType, static_cast<int>(nDataSize),
517 : pabyDst, eDataType, static_cast<int>(nTilePixelSize),
518 : nRawXSize);
519 : }
520 431 : ++poTile->second.nBandsWritten;
521 :
522 : // Write to disk if tile is finished
523 431 : if (poTile->second.nBandsWritten == poGDS->nBands)
524 : {
525 167 : poGDS->WriteTile(nBlockXOff, nBlockYOff, pabyTileData, nTileSize,
526 : nRawXSize, nRawYSize);
527 167 : poGDS->oUnfinishedTiles.erase(poTile);
528 : }
529 : #ifdef DEBUG
530 431 : CPLDebug("RMF", "poGDS->oUnfinishedTiles.size() %d",
531 431 : static_cast<int>(poGDS->oUnfinishedTiles.size()));
532 : #endif // DEBUG
533 : }
534 :
535 431 : return CE_None;
536 : }
537 :
538 : /************************************************************************/
539 : /* GetNoDataValue() */
540 : /************************************************************************/
541 :
542 191 : double RMFRasterBand::GetNoDataValue(int *pbSuccess)
543 :
544 : {
545 191 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
546 :
547 191 : if (pbSuccess)
548 164 : *pbSuccess = TRUE;
549 :
550 191 : return poGDS->sHeader.dfNoData;
551 : }
552 :
553 27 : CPLErr RMFRasterBand::SetNoDataValue(double dfNoData)
554 : {
555 27 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
556 :
557 27 : poGDS->sHeader.dfNoData = dfNoData;
558 27 : poGDS->bHeaderDirty = true;
559 :
560 27 : return CE_None;
561 : }
562 :
563 : /************************************************************************/
564 : /* GetUnitType() */
565 : /************************************************************************/
566 :
567 10 : const char *RMFRasterBand::GetUnitType()
568 :
569 : {
570 10 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
571 :
572 10 : return poGDS->pszUnitType;
573 : }
574 :
575 : /************************************************************************/
576 : /* SetUnitType() */
577 : /************************************************************************/
578 :
579 3 : CPLErr RMFRasterBand::SetUnitType(const char *pszNewValue)
580 :
581 : {
582 3 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
583 3 : int bSuccess = FALSE;
584 3 : int iNewUnit = RMFStrToUnitType(pszNewValue, &bSuccess);
585 :
586 3 : if (bSuccess)
587 : {
588 2 : CPLFree(poGDS->pszUnitType);
589 2 : poGDS->pszUnitType = CPLStrdup(pszNewValue);
590 2 : poGDS->sHeader.iElevationUnit = iNewUnit;
591 2 : poGDS->bHeaderDirty = true;
592 2 : return CE_None;
593 : }
594 : else
595 : {
596 1 : CPLError(CE_Warning, CPLE_NotSupported,
597 : "RMF driver does not support '%s' elevation units. "
598 : "Possible values are: m, dm, cm, mm.",
599 : pszNewValue);
600 1 : return CE_Failure;
601 : }
602 : }
603 :
604 : /************************************************************************/
605 : /* GetColorTable() */
606 : /************************************************************************/
607 :
608 28 : GDALColorTable *RMFRasterBand::GetColorTable()
609 : {
610 28 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
611 :
612 28 : return poGDS->poColorTable;
613 : }
614 :
615 : /************************************************************************/
616 : /* SetColorTable() */
617 : /************************************************************************/
618 :
619 8 : CPLErr RMFRasterBand::SetColorTable(GDALColorTable *poColorTable)
620 : {
621 8 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
622 :
623 8 : if (poColorTable)
624 : {
625 8 : if (poGDS->eRMFType == RMFT_RSW && poGDS->nBands == 1)
626 : {
627 8 : if (!poGDS->pabyColorTable)
628 0 : return CE_Failure;
629 :
630 : GDALColorEntry oEntry;
631 2056 : for (GUInt32 i = 0; i < poGDS->nColorTableSize; i++)
632 : {
633 2048 : poColorTable->GetColorEntryAsRGB(i, &oEntry);
634 : // Red
635 2048 : poGDS->pabyColorTable[i * 4 + 0] =
636 2048 : static_cast<GByte>(oEntry.c1);
637 : // Green
638 2048 : poGDS->pabyColorTable[i * 4 + 1] =
639 2048 : static_cast<GByte>(oEntry.c2);
640 : // Blue
641 2048 : poGDS->pabyColorTable[i * 4 + 2] =
642 2048 : static_cast<GByte>(oEntry.c3);
643 2048 : poGDS->pabyColorTable[i * 4 + 3] = 0;
644 : }
645 :
646 8 : poGDS->bHeaderDirty = true;
647 : }
648 8 : return CE_None;
649 : }
650 :
651 0 : return CE_Failure;
652 : }
653 :
654 59 : int RMFRasterBand::GetOverviewCount()
655 : {
656 59 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
657 59 : if (poGDS->poOvrDatasets.empty())
658 0 : return GDALRasterBand::GetOverviewCount();
659 : else
660 59 : return static_cast<int>(poGDS->poOvrDatasets.size());
661 : }
662 :
663 79 : GDALRasterBand *RMFRasterBand::GetOverview(int i)
664 : {
665 79 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
666 79 : size_t n = static_cast<size_t>(i);
667 79 : if (poGDS->poOvrDatasets.empty())
668 0 : return GDALRasterBand::GetOverview(i);
669 : else
670 79 : return poGDS->poOvrDatasets[n]->GetRasterBand(nBand);
671 : }
672 :
673 694 : CPLErr RMFRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
674 : int nXSize, int nYSize, void *pData,
675 : int nBufXSize, int nBufYSize,
676 : GDALDataType eType, GSpacing nPixelSpace,
677 : GSpacing nLineSpace,
678 : GDALRasterIOExtraArg *psExtraArg)
679 : {
680 694 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
681 :
682 743 : if (eRWFlag == GF_Read && poGDS->poCompressData != nullptr &&
683 49 : poGDS->poCompressData->oThreadPool.GetThreadCount() > 0)
684 : {
685 9 : poGDS->poCompressData->oThreadPool.WaitCompletion();
686 : }
687 :
688 694 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
689 : pData, nBufXSize, nBufYSize, eType,
690 694 : nPixelSpace, nLineSpace, psExtraArg);
691 : }
692 :
693 : /************************************************************************/
694 : /* GetColorInterpretation() */
695 : /************************************************************************/
696 :
697 104 : GDALColorInterp RMFRasterBand::GetColorInterpretation()
698 : {
699 104 : RMFDataset *poGDS = cpl::down_cast<RMFDataset *>(poDS);
700 :
701 104 : if (poGDS->nBands == 3)
702 : {
703 53 : if (nBand == 1)
704 18 : return GCI_RedBand;
705 35 : else if (nBand == 2)
706 17 : return GCI_GreenBand;
707 18 : else if (nBand == 3)
708 18 : return GCI_BlueBand;
709 :
710 0 : return GCI_Undefined;
711 : }
712 :
713 51 : if (poGDS->eRMFType == RMFT_RSW)
714 28 : return GCI_PaletteIndex;
715 :
716 23 : return GCI_Undefined;
717 : }
718 :
719 : /************************************************************************/
720 : /* ==================================================================== */
721 : /* RMFDataset */
722 : /* ==================================================================== */
723 : /************************************************************************/
724 :
725 : /************************************************************************/
726 : /* RMFDataset() */
727 : /************************************************************************/
728 :
729 297 : RMFDataset::RMFDataset() : pszUnitType(CPLStrdup(RMF_UnitsEmpty))
730 : {
731 297 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
732 297 : nBands = 0;
733 297 : memset(&sHeader, 0, sizeof(sHeader));
734 297 : memset(&sExtHeader, 0, sizeof(sExtHeader));
735 297 : }
736 :
737 : /************************************************************************/
738 : /* ~RMFDataset() */
739 : /************************************************************************/
740 :
741 594 : RMFDataset::~RMFDataset()
742 : {
743 297 : RMFDataset::FlushCache(true);
744 404 : for (size_t n = 0; n != poOvrDatasets.size(); ++n)
745 : {
746 107 : poOvrDatasets[n]->RMFDataset::FlushCache(true);
747 : }
748 :
749 297 : VSIFree(paiTiles);
750 297 : VSIFree(pabyDecompressBuffer);
751 297 : VSIFree(pabyCurrentTile);
752 297 : CPLFree(pszUnitType);
753 297 : CPLFree(pabyColorTable);
754 297 : if (poColorTable != nullptr)
755 68 : delete poColorTable;
756 :
757 404 : for (size_t n = 0; n != poOvrDatasets.size(); ++n)
758 : {
759 107 : GDALClose(poOvrDatasets[n]);
760 : }
761 :
762 297 : if (fp != nullptr && poParentDS == nullptr)
763 : {
764 177 : VSIFCloseL(fp);
765 : }
766 594 : }
767 :
768 : /************************************************************************/
769 : /* GetGeoTransform() */
770 : /************************************************************************/
771 :
772 47 : CPLErr RMFDataset::GetGeoTransform(GDALGeoTransform >) const
773 : {
774 47 : gt = m_gt;
775 :
776 47 : if (sHeader.iGeorefFlag)
777 41 : return CE_None;
778 :
779 6 : return CE_Failure;
780 : }
781 :
782 : /************************************************************************/
783 : /* SetGeoTransform() */
784 : /************************************************************************/
785 :
786 41 : CPLErr RMFDataset::SetGeoTransform(const GDALGeoTransform >)
787 : {
788 41 : m_gt = gt;
789 41 : sHeader.dfPixelSize = m_gt[1];
790 41 : if (sHeader.dfPixelSize != 0.0)
791 41 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
792 41 : sHeader.dfLLX = m_gt[0];
793 41 : sHeader.dfLLY = m_gt[3] - nRasterYSize * sHeader.dfPixelSize;
794 41 : sHeader.iGeorefFlag = 1;
795 :
796 41 : bHeaderDirty = true;
797 :
798 41 : return CE_None;
799 : }
800 :
801 : /************************************************************************/
802 : /* GetSpatialRef() */
803 : /************************************************************************/
804 :
805 34 : const OGRSpatialReference *RMFDataset::GetSpatialRef() const
806 :
807 : {
808 34 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
809 : }
810 :
811 : /************************************************************************/
812 : /* SetSpatialRef() */
813 : /************************************************************************/
814 :
815 41 : CPLErr RMFDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
816 :
817 : {
818 41 : m_oSRS.Clear();
819 41 : if (poSRS)
820 41 : m_oSRS = *poSRS;
821 :
822 41 : bHeaderDirty = true;
823 :
824 41 : return CE_None;
825 : }
826 :
827 : /************************************************************************/
828 : /* WriteHeader() */
829 : /************************************************************************/
830 :
831 200 : CPLErr RMFDataset::WriteHeader()
832 : {
833 : /* -------------------------------------------------------------------- */
834 : /* Setup projection. */
835 : /* -------------------------------------------------------------------- */
836 200 : if (!m_oSRS.IsEmpty())
837 : {
838 52 : long iProjection = 0;
839 52 : long iDatum = 0;
840 52 : long iEllips = 0;
841 52 : long iZone = 0;
842 52 : int iVertCS = 0;
843 52 : double adfPrjParams[7] = {};
844 :
845 52 : m_oSRS.exportToPanorama(&iProjection, &iDatum, &iEllips, &iZone,
846 : adfPrjParams);
847 52 : m_oSRS.exportVertCSToPanorama(&iVertCS);
848 52 : sHeader.iProjection = static_cast<GInt32>(iProjection);
849 52 : sHeader.dfStdP1 = adfPrjParams[0];
850 52 : sHeader.dfStdP2 = adfPrjParams[1];
851 52 : sHeader.dfCenterLat = adfPrjParams[2];
852 52 : sHeader.dfCenterLong = adfPrjParams[3];
853 52 : if (m_oSRS.GetAuthorityName(nullptr) != nullptr &&
854 62 : m_oSRS.GetAuthorityCode(nullptr) != nullptr &&
855 10 : EQUAL(m_oSRS.GetAuthorityName(nullptr), "EPSG"))
856 : {
857 10 : sHeader.iEPSGCode = atoi(m_oSRS.GetAuthorityCode(nullptr));
858 : }
859 :
860 52 : sExtHeader.nEllipsoid = static_cast<GInt32>(iEllips);
861 52 : sExtHeader.nDatum = static_cast<GInt32>(iDatum);
862 52 : sExtHeader.nZone = static_cast<GInt32>(iZone);
863 52 : sExtHeader.nVertDatum = static_cast<GInt32>(iVertCS);
864 :
865 : // Set map type
866 52 : auto pszMapType = GetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY);
867 52 : if (pszMapType != nullptr)
868 : {
869 31 : sHeader.iMapType = static_cast<GInt32>(atoi(pszMapType));
870 : }
871 : }
872 :
873 : #define RMF_WRITE_LONG(ptr, value, offset) \
874 : do \
875 : { \
876 : GInt32 iLong = CPL_LSBWORD32(value); \
877 : memcpy((ptr) + (offset), &iLong, 4); \
878 : } while (false);
879 :
880 : #define RMF_WRITE_ULONG(ptr, value, offset) \
881 : do \
882 : { \
883 : GUInt32 iULong = CPL_LSBWORD32(value); \
884 : memcpy((ptr) + (offset), &iULong, 4); \
885 : } while (false);
886 :
887 : #define RMF_WRITE_DOUBLE(ptr, value, offset) \
888 : do \
889 : { \
890 : double dfDouble = (value); \
891 : CPL_LSBPTR64(&dfDouble); \
892 : memcpy((ptr) + (offset), &dfDouble, 8); \
893 : } while (false);
894 :
895 : // Frame if present
896 400 : std::vector<RSWFrameCoord> astFrameCoords;
897 200 : auto pszFrameWKT = GetMetadataItem(MD_FRAME_KEY);
898 200 : if (pszFrameWKT != nullptr)
899 : {
900 2 : CPLDebug("RMF", "Write to header frame: %s", pszFrameWKT);
901 2 : OGRGeometry *poFrameGeom = nullptr;
902 2 : if (OGRGeometryFactory::createFromWkt(pszFrameWKT, nullptr,
903 2 : &poFrameGeom) == OGRERR_NONE)
904 : {
905 2 : if (poFrameGeom->getGeometryType() == wkbPolygon)
906 : {
907 2 : GDALGeoTransform reverseGT;
908 2 : if (m_gt.GetInverse(reverseGT))
909 : {
910 2 : OGRPolygon *poFramePoly = poFrameGeom->toPolygon();
911 2 : if (!poFramePoly->IsEmpty())
912 : {
913 : OGRLinearRing *poFrameRing =
914 2 : poFramePoly->getExteriorRing();
915 12 : for (int i = 0; i < poFrameRing->getNumPoints(); i++)
916 : {
917 : int nX =
918 10 : int(reverseGT[0] +
919 10 : poFrameRing->getX(i) * reverseGT[1] - 0.5);
920 : int nY =
921 10 : int(reverseGT[3] +
922 10 : poFrameRing->getY(i) * reverseGT[5] - 0.5);
923 :
924 10 : CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
925 :
926 10 : astFrameCoords.push_back({nX, nY});
927 : }
928 : }
929 :
930 4 : if (astFrameCoords.empty() ||
931 2 : astFrameCoords.size() > nMaxFramePointCount)
932 : {
933 : // CPLError(CE_Warning, CPLE_AppDefined, "Invalid frame WKT: %s", pszFrameWKT);
934 0 : CPLDebug("RMF", "Write to header frame failed: no "
935 : "points or too many");
936 0 : astFrameCoords.clear();
937 : }
938 : else
939 : {
940 2 : sHeader.nROISize = static_cast<GUInt32>(
941 2 : sizeof(RSWFrame) +
942 : sizeof(RSWFrameCoord) *
943 : astFrameCoords
944 2 : .size()); // Set real size and real point count
945 2 : sHeader.iFrameFlag = 0;
946 : }
947 : }
948 : else
949 : {
950 0 : CPLDebug("RMF", "Write to header frame failed: "
951 : "GDALInvGeoTransform == FALSE");
952 : }
953 : }
954 2 : OGRGeometryFactory::destroyGeometry(poFrameGeom);
955 : }
956 : else
957 : {
958 0 : CPLDebug("RMF", "Write to header frame failed: "
959 : "OGRGeometryFactory::createFromWkt error");
960 : }
961 : }
962 :
963 200 : vsi_l_offset iCurrentFileSize(GetLastOffset());
964 200 : sHeader.nFileSize0 = GetRMFOffset(iCurrentFileSize, &iCurrentFileSize);
965 200 : sHeader.nSize = sHeader.nFileSize0 - GetRMFOffset(nHeaderOffset, nullptr);
966 : /* -------------------------------------------------------------------- */
967 : /* Write out the main header. */
968 : /* -------------------------------------------------------------------- */
969 : {
970 200 : GByte abyHeader[RMF_HEADER_SIZE] = {};
971 :
972 200 : memcpy(abyHeader, sHeader.bySignature, RMF_SIGNATURE_SIZE);
973 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iVersion, 4);
974 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nSize, 8);
975 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nOvrOffset, 12);
976 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iUserID, 16);
977 200 : memcpy(abyHeader + 20, sHeader.byName, RMF_NAME_SIZE);
978 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nBitDepth, 52);
979 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nHeight, 56);
980 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nWidth, 60);
981 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nXTiles, 64);
982 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nYTiles, 68);
983 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileHeight, 72);
984 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileWidth, 76);
985 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileHeight, 80);
986 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nLastTileWidth, 84);
987 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nROIOffset, 88);
988 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nROISize, 92);
989 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblOffset, 96);
990 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nClrTblSize, 100);
991 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblOffset, 104);
992 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nTileTblSize, 108);
993 200 : RMF_WRITE_LONG(abyHeader, sHeader.iMapType, 124);
994 200 : RMF_WRITE_LONG(abyHeader, sHeader.iProjection, 128);
995 200 : RMF_WRITE_LONG(abyHeader, sHeader.iEPSGCode, 132);
996 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfScale, 136);
997 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfResolution, 144);
998 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfPixelSize, 152);
999 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLY, 160);
1000 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfLLX, 168);
1001 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP1, 176);
1002 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfStdP2, 184);
1003 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLong, 192);
1004 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfCenterLat, 200);
1005 200 : *(abyHeader + 208) = sHeader.iCompression;
1006 200 : *(abyHeader + 209) = sHeader.iMaskType;
1007 200 : *(abyHeader + 210) = sHeader.iMaskStep;
1008 200 : *(abyHeader + 211) = sHeader.iFrameFlag;
1009 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblOffset, 212);
1010 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFlagsTblSize, 216);
1011 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize0, 220);
1012 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nFileSize1, 224);
1013 200 : *(abyHeader + 228) = sHeader.iUnknown;
1014 200 : *(abyHeader + 244) = sHeader.iGeorefFlag;
1015 200 : *(abyHeader + 245) = sHeader.iInverse;
1016 200 : *(abyHeader + 246) = sHeader.iJpegQuality;
1017 200 : memcpy(abyHeader + 248, sHeader.abyInvisibleColors,
1018 : sizeof(sHeader.abyInvisibleColors));
1019 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[0], 280);
1020 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.adfElevMinMax[1], 288);
1021 200 : RMF_WRITE_DOUBLE(abyHeader, sHeader.dfNoData, 296);
1022 200 : RMF_WRITE_ULONG(abyHeader, sHeader.iElevationUnit, 304);
1023 200 : *(abyHeader + 308) = sHeader.iElevationType;
1024 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrOffset, 312);
1025 200 : RMF_WRITE_ULONG(abyHeader, sHeader.nExtHdrSize, 316);
1026 :
1027 200 : VSIFSeekL(fp, nHeaderOffset, SEEK_SET);
1028 200 : VSIFWriteL(abyHeader, 1, sizeof(abyHeader), fp);
1029 : }
1030 :
1031 : /* -------------------------------------------------------------------- */
1032 : /* Write out the extended header. */
1033 : /* -------------------------------------------------------------------- */
1034 :
1035 200 : if (sHeader.nExtHdrOffset && sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
1036 : {
1037 200 : if (sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
1038 : {
1039 0 : CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
1040 0 : return CE_Failure;
1041 : }
1042 : GByte *pabyExtHeader =
1043 200 : static_cast<GByte *>(CPLCalloc(sHeader.nExtHdrSize, 1));
1044 :
1045 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nEllipsoid, 24);
1046 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nVertDatum, 28);
1047 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nDatum, 32);
1048 200 : RMF_WRITE_LONG(pabyExtHeader, sExtHeader.nZone, 36);
1049 :
1050 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nExtHdrOffset), SEEK_SET);
1051 200 : VSIFWriteL(pabyExtHeader, 1, sHeader.nExtHdrSize, fp);
1052 :
1053 200 : CPLFree(pabyExtHeader);
1054 : }
1055 :
1056 : /* -------------------------------------------------------------------- */
1057 : /* Write out the color table. */
1058 : /* -------------------------------------------------------------------- */
1059 :
1060 200 : if (sHeader.nClrTblOffset && sHeader.nClrTblSize)
1061 : {
1062 59 : VSIFSeekL(fp, GetFileOffset(sHeader.nClrTblOffset), SEEK_SET);
1063 59 : VSIFWriteL(pabyColorTable, 1, sHeader.nClrTblSize, fp);
1064 : }
1065 :
1066 200 : if (sHeader.nROIOffset && sHeader.nROISize)
1067 : {
1068 2 : GByte *pabyROI = static_cast<GByte *>(CPLCalloc(sHeader.nROISize, 1));
1069 2 : memset(pabyROI, 0, sHeader.nROISize);
1070 :
1071 2 : auto nPointCount = astFrameCoords.size();
1072 2 : size_t offset = 0;
1073 2 : RMF_WRITE_LONG(pabyROI, nPolygonType, offset);
1074 2 : offset += 4;
1075 2 : RMF_WRITE_LONG(pabyROI, static_cast<GInt32>((4 + nPointCount * 2) * 4),
1076 : offset);
1077 2 : offset += 4;
1078 2 : RMF_WRITE_LONG(pabyROI, 0, offset);
1079 2 : offset += 4;
1080 2 : RMF_WRITE_LONG(pabyROI, static_cast<GInt32>(32768 * nPointCount * 2),
1081 : offset);
1082 2 : offset += 4;
1083 :
1084 : // Write points
1085 12 : for (size_t i = 0; i < nPointCount; i++)
1086 : {
1087 10 : RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nX, offset);
1088 10 : offset += 4;
1089 10 : RMF_WRITE_LONG(pabyROI, astFrameCoords[i].nY, offset);
1090 10 : offset += 4;
1091 : }
1092 :
1093 2 : VSIFSeekL(fp, GetFileOffset(sHeader.nROIOffset), SEEK_SET);
1094 2 : VSIFWriteL(pabyROI, 1, sHeader.nROISize, fp);
1095 :
1096 2 : CPLFree(pabyROI);
1097 : }
1098 :
1099 200 : if (sHeader.nFlagsTblOffset && sHeader.nFlagsTblSize)
1100 : {
1101 : GByte *pabyFlagsTbl =
1102 200 : static_cast<GByte *>(CPLCalloc(sHeader.nFlagsTblSize, 1));
1103 :
1104 200 : if (sHeader.iFrameFlag == 0)
1105 : {
1106 : // TODO: Add more strictly check for flag value
1107 2 : memset(
1108 : pabyFlagsTbl, 2,
1109 : sHeader
1110 2 : .nFlagsTblSize); // Mark all blocks as intersected with ROI. 0 - complete outside, 1 - complete inside.
1111 : }
1112 : else
1113 : {
1114 198 : memset(pabyFlagsTbl, 0, sHeader.nFlagsTblSize);
1115 : }
1116 :
1117 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nFlagsTblOffset), SEEK_SET);
1118 200 : VSIFWriteL(pabyFlagsTbl, 1, sHeader.nFlagsTblSize, fp);
1119 :
1120 200 : CPLFree(pabyFlagsTbl);
1121 : }
1122 :
1123 : #undef RMF_WRITE_DOUBLE
1124 : #undef RMF_WRITE_ULONG
1125 : #undef RMF_WRITE_LONG
1126 :
1127 : /* -------------------------------------------------------------------- */
1128 : /* Write out the block table, swap if needed. */
1129 : /* -------------------------------------------------------------------- */
1130 :
1131 200 : VSIFSeekL(fp, GetFileOffset(sHeader.nTileTblOffset), SEEK_SET);
1132 :
1133 : #ifdef CPL_MSB
1134 : GUInt32 *paiTilesSwapped =
1135 : static_cast<GUInt32 *>(CPLMalloc(sHeader.nTileTblSize));
1136 : if (!paiTilesSwapped)
1137 : return CE_Failure;
1138 :
1139 : memcpy(paiTilesSwapped, paiTiles, sHeader.nTileTblSize);
1140 : for (GUInt32 i = 0; i < sHeader.nTileTblSize / sizeof(GUInt32); i++)
1141 : CPL_SWAP32PTR(paiTilesSwapped + i);
1142 : VSIFWriteL(paiTilesSwapped, 1, sHeader.nTileTblSize, fp);
1143 :
1144 : CPLFree(paiTilesSwapped);
1145 : #else
1146 200 : VSIFWriteL(paiTiles, 1, sHeader.nTileTblSize, fp);
1147 : #endif
1148 :
1149 200 : bHeaderDirty = false;
1150 :
1151 200 : return CE_None;
1152 : }
1153 :
1154 : /************************************************************************/
1155 : /* FlushCache() */
1156 : /************************************************************************/
1157 :
1158 411 : CPLErr RMFDataset::FlushCache(bool bAtClosing)
1159 :
1160 : {
1161 411 : CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
1162 :
1163 549 : if (poCompressData != nullptr &&
1164 138 : poCompressData->oThreadPool.GetThreadCount() > 0)
1165 : {
1166 9 : poCompressData->oThreadPool.WaitCompletion();
1167 : }
1168 :
1169 411 : if (bAtClosing && eRMFType == RMFT_MTW && eAccess == GA_Update)
1170 : {
1171 77 : GDALRasterBand *poBand = GetRasterBand(1);
1172 :
1173 77 : if (poBand)
1174 : {
1175 : // ComputeRasterMinMax can setup error in case of dataset full
1176 : // from NoData values, but it makes no sense here.
1177 77 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
1178 77 : poBand->ComputeRasterMinMax(FALSE, sHeader.adfElevMinMax);
1179 77 : bHeaderDirty = true;
1180 : }
1181 : }
1182 411 : if (bHeaderDirty && WriteHeader() != CE_None)
1183 0 : eErr = CE_Failure;
1184 411 : return eErr;
1185 : }
1186 :
1187 : /************************************************************************/
1188 : /* Identify() */
1189 : /************************************************************************/
1190 :
1191 59343 : int RMFDataset::Identify(GDALOpenInfo *poOpenInfo)
1192 :
1193 : {
1194 59343 : if (poOpenInfo->pabyHeader == nullptr)
1195 54138 : return FALSE;
1196 :
1197 5205 : if (memcmp(poOpenInfo->pabyHeader, RMF_SigRSW, sizeof(RMF_SigRSW)) != 0 &&
1198 4996 : memcmp(poOpenInfo->pabyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) !=
1199 4984 : 0 &&
1200 4984 : memcmp(poOpenInfo->pabyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) != 0)
1201 4866 : return FALSE;
1202 :
1203 339 : return TRUE;
1204 : }
1205 :
1206 : /************************************************************************/
1207 : /* Open() */
1208 : /************************************************************************/
1209 :
1210 128 : GDALDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo)
1211 : {
1212 128 : auto poDS = Open(poOpenInfo, nullptr, 0);
1213 128 : if (poDS == nullptr)
1214 : {
1215 9 : return nullptr;
1216 : }
1217 :
1218 119 : RMFDataset *poCurrentLayer = poDS;
1219 119 : RMFDataset *poParent = poCurrentLayer;
1220 119 : const int nMaxPossibleOvCount = 64;
1221 :
1222 200 : for (int iOv = 0; iOv < nMaxPossibleOvCount && poCurrentLayer != nullptr;
1223 : ++iOv)
1224 : {
1225 200 : poCurrentLayer = poCurrentLayer->OpenOverview(poParent, poOpenInfo);
1226 200 : if (poCurrentLayer == nullptr)
1227 119 : break;
1228 81 : poParent->poOvrDatasets.push_back(poCurrentLayer);
1229 : }
1230 :
1231 119 : return poDS;
1232 : }
1233 :
1234 213 : RMFDataset *RMFDataset::Open(GDALOpenInfo *poOpenInfo, RMFDataset *poParentDS,
1235 : vsi_l_offset nNextHeaderOffset)
1236 : {
1237 341 : if (!Identify(poOpenInfo) ||
1238 128 : (poParentDS == nullptr && poOpenInfo->fpL == nullptr))
1239 2 : return nullptr;
1240 :
1241 : /* -------------------------------------------------------------------- */
1242 : /* Create a corresponding GDALDataset. */
1243 : /* -------------------------------------------------------------------- */
1244 211 : RMFDataset *poDS = new RMFDataset();
1245 :
1246 211 : if (poParentDS == nullptr)
1247 : {
1248 128 : poDS->fp = poOpenInfo->fpL;
1249 128 : poOpenInfo->fpL = nullptr;
1250 128 : poDS->nHeaderOffset = 0;
1251 128 : poDS->poParentDS = nullptr;
1252 : }
1253 : else
1254 : {
1255 83 : poDS->fp = poParentDS->fp;
1256 83 : poDS->poParentDS = poParentDS;
1257 83 : poDS->nHeaderOffset = nNextHeaderOffset;
1258 : }
1259 211 : poDS->eAccess = poOpenInfo->eAccess;
1260 :
1261 : #define RMF_READ_SHORT(ptr, value, offset) \
1262 : do \
1263 : { \
1264 : memcpy(&(value), reinterpret_cast<GInt16 *>((ptr) + (offset)), \
1265 : sizeof(GInt16)); \
1266 : if (poDS->bBigEndian) \
1267 : { \
1268 : CPL_MSBPTR16(&(value)); \
1269 : } \
1270 : else \
1271 : { \
1272 : CPL_LSBPTR16(&(value)); \
1273 : } \
1274 : } while (false);
1275 :
1276 : #define RMF_READ_ULONG(ptr, value, offset) \
1277 : do \
1278 : { \
1279 : memcpy(&(value), reinterpret_cast<GUInt32 *>((ptr) + (offset)), \
1280 : sizeof(GUInt32)); \
1281 : if (poDS->bBigEndian) \
1282 : { \
1283 : CPL_MSBPTR32(&(value)); \
1284 : } \
1285 : else \
1286 : { \
1287 : CPL_LSBPTR32(&(value)); \
1288 : } \
1289 : } while (false);
1290 :
1291 : #define RMF_READ_LONG(ptr, value, offset) RMF_READ_ULONG(ptr, value, offset)
1292 :
1293 : #define RMF_READ_DOUBLE(ptr, value, offset) \
1294 : do \
1295 : { \
1296 : memcpy(&(value), reinterpret_cast<double *>((ptr) + (offset)), \
1297 : sizeof(double)); \
1298 : if (poDS->bBigEndian) \
1299 : { \
1300 : CPL_MSBPTR64(&(value)); \
1301 : } \
1302 : else \
1303 : { \
1304 : CPL_LSBPTR64(&(value)); \
1305 : } \
1306 : } while (false);
1307 :
1308 : /* -------------------------------------------------------------------- */
1309 : /* Read the main header. */
1310 : /* -------------------------------------------------------------------- */
1311 :
1312 : {
1313 211 : GByte abyHeader[RMF_HEADER_SIZE] = {};
1314 :
1315 211 : VSIFSeekL(poDS->fp, nNextHeaderOffset, SEEK_SET);
1316 211 : if (VSIFReadL(abyHeader, 1, sizeof(abyHeader), poDS->fp) !=
1317 : sizeof(abyHeader))
1318 : {
1319 0 : delete poDS;
1320 0 : return nullptr;
1321 : }
1322 :
1323 211 : if (memcmp(abyHeader, RMF_SigMTW, sizeof(RMF_SigMTW)) == 0)
1324 : {
1325 76 : poDS->eRMFType = RMFT_MTW;
1326 : }
1327 135 : else if (memcmp(abyHeader, RMF_SigRSW_BE, sizeof(RMF_SigRSW_BE)) == 0)
1328 : {
1329 6 : poDS->eRMFType = RMFT_RSW;
1330 6 : poDS->bBigEndian = true;
1331 : }
1332 : else
1333 : {
1334 129 : poDS->eRMFType = RMFT_RSW;
1335 : }
1336 :
1337 211 : memcpy(poDS->sHeader.bySignature, abyHeader, RMF_SIGNATURE_SIZE);
1338 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iVersion, 4);
1339 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nSize, 8);
1340 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nOvrOffset, 12);
1341 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iUserID, 16);
1342 211 : memcpy(poDS->sHeader.byName, abyHeader + 20,
1343 : sizeof(poDS->sHeader.byName));
1344 211 : poDS->sHeader.byName[sizeof(poDS->sHeader.byName) - 1] = '\0';
1345 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nBitDepth, 52);
1346 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nHeight, 56);
1347 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nWidth, 60);
1348 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nXTiles, 64);
1349 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nYTiles, 68);
1350 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileHeight, 72);
1351 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileWidth, 76);
1352 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileHeight, 80);
1353 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nLastTileWidth, 84);
1354 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nROIOffset, 88);
1355 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nROISize, 92);
1356 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblOffset, 96);
1357 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nClrTblSize, 100);
1358 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblOffset, 104);
1359 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nTileTblSize, 108);
1360 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iMapType, 124);
1361 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iProjection, 128);
1362 211 : RMF_READ_LONG(abyHeader, poDS->sHeader.iEPSGCode, 132);
1363 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfScale, 136);
1364 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfResolution, 144);
1365 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfPixelSize, 152);
1366 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLY, 160);
1367 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfLLX, 168);
1368 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP1, 176);
1369 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfStdP2, 184);
1370 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLong, 192);
1371 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfCenterLat, 200);
1372 211 : poDS->sHeader.iCompression = *(abyHeader + 208);
1373 211 : poDS->sHeader.iMaskType = *(abyHeader + 209);
1374 211 : poDS->sHeader.iMaskStep = *(abyHeader + 210);
1375 211 : poDS->sHeader.iFrameFlag = *(abyHeader + 211);
1376 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblOffset, 212);
1377 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFlagsTblSize, 216);
1378 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize0, 220);
1379 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nFileSize1, 224);
1380 211 : poDS->sHeader.iUnknown = *(abyHeader + 228);
1381 211 : poDS->sHeader.iGeorefFlag = *(abyHeader + 244);
1382 211 : poDS->sHeader.iInverse = *(abyHeader + 245);
1383 211 : poDS->sHeader.iJpegQuality = *(abyHeader + 246);
1384 211 : memcpy(poDS->sHeader.abyInvisibleColors, abyHeader + 248,
1385 : sizeof(poDS->sHeader.abyInvisibleColors));
1386 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[0], 280);
1387 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.adfElevMinMax[1], 288);
1388 211 : RMF_READ_DOUBLE(abyHeader, poDS->sHeader.dfNoData, 296);
1389 :
1390 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.iElevationUnit, 304);
1391 211 : poDS->sHeader.iElevationType = *(abyHeader + 308);
1392 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrOffset, 312);
1393 211 : RMF_READ_ULONG(abyHeader, poDS->sHeader.nExtHdrSize, 316);
1394 211 : poDS->SetMetadataItem(MD_SCALE_KEY,
1395 211 : CPLSPrintf("1 : %u", int(poDS->sHeader.dfScale)));
1396 211 : poDS->SetMetadataItem(MD_NAME_KEY,
1397 211 : CPLSPrintf("%s", poDS->sHeader.byName));
1398 211 : poDS->SetMetadataItem(MD_VERSION_KEY,
1399 : CPLSPrintf("%d", poDS->sHeader.iVersion));
1400 211 : poDS->SetMetadataItem(MD_MATH_BASE_MAP_TYPE_KEY,
1401 : CPLSPrintf("%d", poDS->sHeader.iMapType));
1402 211 : poDS->SetMetadataItem(MD_MATH_BASE_PROJECTION_KEY,
1403 : CPLSPrintf("%d", poDS->sHeader.iProjection));
1404 : }
1405 :
1406 211 : if (poDS->sHeader.nTileTblSize % (sizeof(GUInt32) * 2))
1407 : {
1408 0 : CPLError(CE_Warning, CPLE_IllegalArg, "Invalid tile table size.");
1409 0 : delete poDS;
1410 0 : return nullptr;
1411 : }
1412 :
1413 : bool bInvalidTileSize;
1414 : try
1415 : {
1416 : uint64_t nMaxTileBits =
1417 211 : (CPLSM(static_cast<uint64_t>(2)) *
1418 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileWidth)) *
1419 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nTileHeight)) *
1420 422 : CPLSM(static_cast<uint64_t>(poDS->sHeader.nBitDepth)))
1421 211 : .v();
1422 211 : bInvalidTileSize =
1423 : (nMaxTileBits >
1424 211 : static_cast<uint64_t>(std::numeric_limits<GUInt32>::max()));
1425 : }
1426 0 : catch (...)
1427 : {
1428 0 : bInvalidTileSize = true;
1429 : }
1430 211 : if (bInvalidTileSize)
1431 : {
1432 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1433 : "Invalid tile size. Width %lu, height %lu, bit depth %lu.",
1434 0 : static_cast<unsigned long>(poDS->sHeader.nTileWidth),
1435 0 : static_cast<unsigned long>(poDS->sHeader.nTileHeight),
1436 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1437 0 : delete poDS;
1438 0 : return nullptr;
1439 : }
1440 :
1441 211 : if (poDS->sHeader.nLastTileWidth > poDS->sHeader.nTileWidth ||
1442 211 : poDS->sHeader.nLastTileHeight > poDS->sHeader.nTileHeight)
1443 : {
1444 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1445 : "Invalid last tile size %lu x %lu. "
1446 : "It can't be greater than %lu x %lu.",
1447 0 : static_cast<unsigned long>(poDS->sHeader.nLastTileWidth),
1448 0 : static_cast<unsigned long>(poDS->sHeader.nLastTileHeight),
1449 0 : static_cast<unsigned long>(poDS->sHeader.nTileWidth),
1450 0 : static_cast<unsigned long>(poDS->sHeader.nTileHeight));
1451 0 : delete poDS;
1452 0 : return nullptr;
1453 : }
1454 :
1455 211 : if (poParentDS != nullptr)
1456 : {
1457 83 : if (0 != memcmp(poDS->sHeader.bySignature,
1458 83 : poParentDS->sHeader.bySignature, RMF_SIGNATURE_SIZE))
1459 : {
1460 2 : CPLError(CE_Warning, CPLE_IllegalArg,
1461 : "Invalid subheader signature.");
1462 2 : delete poDS;
1463 2 : return nullptr;
1464 : }
1465 : }
1466 :
1467 : /* -------------------------------------------------------------------- */
1468 : /* Read the extended header. */
1469 : /* -------------------------------------------------------------------- */
1470 :
1471 209 : if (poDS->sHeader.nExtHdrOffset &&
1472 198 : poDS->sHeader.nExtHdrSize >= RMF_MIN_EXT_HEADER_SIZE)
1473 : {
1474 198 : if (poDS->sHeader.nExtHdrSize > RMF_MAX_EXT_HEADER_SIZE)
1475 : {
1476 0 : CPLError(CE_Failure, CPLE_FileIO, "RMF File malformed");
1477 0 : delete poDS;
1478 0 : return nullptr;
1479 : }
1480 : GByte *pabyExtHeader =
1481 198 : static_cast<GByte *>(CPLCalloc(poDS->sHeader.nExtHdrSize, 1));
1482 198 : if (pabyExtHeader == nullptr)
1483 : {
1484 0 : delete poDS;
1485 0 : return nullptr;
1486 : }
1487 :
1488 198 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nExtHdrOffset),
1489 : SEEK_SET);
1490 198 : VSIFReadL(pabyExtHeader, 1, poDS->sHeader.nExtHdrSize, poDS->fp);
1491 :
1492 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nEllipsoid, 24);
1493 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nVertDatum, 28);
1494 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nDatum, 32);
1495 198 : RMF_READ_LONG(pabyExtHeader, poDS->sExtHeader.nZone, 36);
1496 :
1497 198 : CPLFree(pabyExtHeader);
1498 : }
1499 :
1500 209 : CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
1501 :
1502 209 : constexpr GUInt32 ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE =
1503 : 10 * 1024 * 1024;
1504 : #ifdef DEBUG
1505 :
1506 418 : CPLDebug("RMF",
1507 : "%s image has width %d, height %d, bit depth %d, "
1508 : "compression scheme %d, %s, nodata %f",
1509 209 : (poDS->eRMFType == RMFT_MTW) ? "MTW" : "RSW", poDS->sHeader.nWidth,
1510 : poDS->sHeader.nHeight, poDS->sHeader.nBitDepth,
1511 209 : poDS->sHeader.iCompression,
1512 209 : poDS->bBigEndian ? "big endian" : "little endian",
1513 : poDS->sHeader.dfNoData);
1514 209 : CPLDebug("RMF",
1515 : "Size %d, offset to overview %#lx, user ID %d, "
1516 : "ROI offset %#lx, ROI size %d",
1517 : poDS->sHeader.nSize,
1518 209 : static_cast<unsigned long>(poDS->sHeader.nOvrOffset),
1519 : poDS->sHeader.iUserID,
1520 209 : static_cast<unsigned long>(poDS->sHeader.nROIOffset),
1521 : poDS->sHeader.nROISize);
1522 209 : CPLDebug("RMF", "Map type %d, projection %d, scale %f, resolution %f, ",
1523 : poDS->sHeader.iMapType, poDS->sHeader.iProjection,
1524 : poDS->sHeader.dfScale, poDS->sHeader.dfResolution);
1525 209 : CPLDebug("RMF", "EPSG %d ", poDS->sHeader.iEPSGCode);
1526 209 : CPLDebug("RMF", "Georeferencing: pixel size %f, LLX %f, LLY %f",
1527 : poDS->sHeader.dfPixelSize, poDS->sHeader.dfLLX,
1528 : poDS->sHeader.dfLLY);
1529 :
1530 209 : if (poDS->sHeader.nROIOffset &&
1531 116 : poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
1532 12 : poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
1533 : {
1534 : GByte *pabyROI = reinterpret_cast<GByte *>(
1535 12 : VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
1536 12 : if (pabyROI == nullptr)
1537 : {
1538 0 : delete poDS;
1539 0 : return nullptr;
1540 : }
1541 :
1542 12 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
1543 : SEEK_SET);
1544 12 : if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
1545 : {
1546 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
1547 0 : CPLFree(pabyROI);
1548 0 : delete poDS;
1549 0 : return nullptr;
1550 : }
1551 :
1552 : GInt32 nValue;
1553 :
1554 12 : CPLDebug("RMF", "ROI coordinates:");
1555 : /* coverity[tainted_data] */
1556 3504 : for (GUInt32 i = 0; i + sizeof(nValue) <= poDS->sHeader.nROISize;
1557 3492 : i += sizeof(nValue))
1558 : {
1559 3492 : RMF_READ_LONG(pabyROI, nValue, i);
1560 3492 : CPLDebug("RMF", "%d", nValue);
1561 : }
1562 :
1563 12 : CPLFree(pabyROI);
1564 : }
1565 : #endif
1566 418 : if (poDS->sHeader.nWidth >= INT_MAX || poDS->sHeader.nHeight >= INT_MAX ||
1567 209 : !GDALCheckDatasetDimensions(poDS->sHeader.nWidth,
1568 209 : poDS->sHeader.nHeight))
1569 : {
1570 0 : delete poDS;
1571 0 : return nullptr;
1572 : }
1573 :
1574 : /* -------------------------------------------------------------------- */
1575 : /* Read array of blocks offsets/sizes. */
1576 : /* -------------------------------------------------------------------- */
1577 :
1578 : // To avoid useless excessive memory allocation
1579 209 : if (poDS->sHeader.nTileTblSize > 1000000)
1580 : {
1581 0 : VSIFSeekL(poDS->fp, 0, SEEK_END);
1582 0 : vsi_l_offset nFileSize = VSIFTellL(poDS->fp);
1583 0 : if (nFileSize < poDS->sHeader.nTileTblSize)
1584 : {
1585 0 : delete poDS;
1586 0 : return nullptr;
1587 : }
1588 : }
1589 :
1590 209 : if (VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nTileTblOffset),
1591 209 : SEEK_SET) < 0)
1592 : {
1593 0 : delete poDS;
1594 0 : return nullptr;
1595 : }
1596 :
1597 209 : poDS->paiTiles =
1598 209 : reinterpret_cast<GUInt32 *>(VSIMalloc(poDS->sHeader.nTileTblSize));
1599 209 : if (!poDS->paiTiles)
1600 : {
1601 0 : delete poDS;
1602 0 : return nullptr;
1603 : }
1604 :
1605 209 : if (VSIFReadL(poDS->paiTiles, 1, poDS->sHeader.nTileTblSize, poDS->fp) <
1606 209 : poDS->sHeader.nTileTblSize)
1607 : {
1608 9 : CPLDebug("RMF", "Can't read tiles offsets/sizes table.");
1609 9 : delete poDS;
1610 9 : return nullptr;
1611 : }
1612 :
1613 : #ifdef CPL_MSB
1614 : if (!poDS->bBigEndian)
1615 : {
1616 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1617 : i++)
1618 : CPL_SWAP32PTR(poDS->paiTiles + i);
1619 : }
1620 : #else
1621 200 : if (poDS->bBigEndian)
1622 : {
1623 30 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1624 : i++)
1625 24 : CPL_SWAP32PTR(poDS->paiTiles + i);
1626 : }
1627 : #endif
1628 :
1629 : #ifdef DEBUG
1630 200 : CPLDebug("RMF", "List of block offsets/sizes:");
1631 :
1632 646 : for (GUInt32 i = 0; i < poDS->sHeader.nTileTblSize / sizeof(GUInt32);
1633 446 : i += 2)
1634 : {
1635 446 : CPLDebug("RMF", " %u / %u", poDS->paiTiles[i],
1636 446 : poDS->paiTiles[i + 1]);
1637 : }
1638 : #endif
1639 :
1640 : /* -------------------------------------------------------------------- */
1641 : /* Set up essential image parameters. */
1642 : /* -------------------------------------------------------------------- */
1643 200 : GDALDataType eType = GDT_Byte;
1644 :
1645 200 : poDS->nRasterXSize = poDS->sHeader.nWidth;
1646 200 : poDS->nRasterYSize = poDS->sHeader.nHeight;
1647 :
1648 200 : if (poDS->eRMFType == RMFT_RSW)
1649 : {
1650 126 : switch (poDS->sHeader.nBitDepth)
1651 : {
1652 58 : case 32:
1653 : case 24:
1654 : case 16:
1655 58 : poDS->nBands = 3;
1656 58 : break;
1657 68 : case 1:
1658 : case 4:
1659 : case 8:
1660 68 : if (poParentDS != nullptr &&
1661 26 : poParentDS->poColorTable != nullptr)
1662 : {
1663 26 : poDS->poColorTable = poParentDS->poColorTable->Clone();
1664 : }
1665 : else
1666 : {
1667 : // Allocate memory for colour table and read it
1668 42 : poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
1669 42 : GUInt32 nExpectedColorTableBytes =
1670 42 : poDS->nColorTableSize * 4;
1671 42 : if (nExpectedColorTableBytes > poDS->sHeader.nClrTblSize)
1672 : {
1673 : // We could probably test for strict equality in
1674 : // the above test ???
1675 0 : CPLDebug("RMF",
1676 : "Wrong color table size. "
1677 : "Expected %u, got %u.",
1678 : nExpectedColorTableBytes,
1679 : poDS->sHeader.nClrTblSize);
1680 0 : delete poDS;
1681 0 : return nullptr;
1682 : }
1683 42 : poDS->pabyColorTable = reinterpret_cast<GByte *>(
1684 42 : VSIMalloc(nExpectedColorTableBytes));
1685 42 : if (poDS->pabyColorTable == nullptr)
1686 : {
1687 0 : CPLDebug("RMF", "Can't allocate color table.");
1688 0 : delete poDS;
1689 0 : return nullptr;
1690 : }
1691 42 : if (VSIFSeekL(
1692 : poDS->fp,
1693 : poDS->GetFileOffset(poDS->sHeader.nClrTblOffset),
1694 42 : SEEK_SET) < 0)
1695 : {
1696 0 : CPLDebug("RMF", "Can't seek to color table location.");
1697 0 : delete poDS;
1698 0 : return nullptr;
1699 : }
1700 42 : if (VSIFReadL(poDS->pabyColorTable, 1,
1701 : nExpectedColorTableBytes,
1702 42 : poDS->fp) < nExpectedColorTableBytes)
1703 : {
1704 0 : CPLDebug("RMF", "Can't read color table.");
1705 0 : delete poDS;
1706 0 : return nullptr;
1707 : }
1708 :
1709 42 : poDS->poColorTable = new GDALColorTable();
1710 9326 : for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
1711 : {
1712 9284 : const GDALColorEntry oEntry = {
1713 9284 : poDS->pabyColorTable[i * 4], // Red
1714 9284 : poDS->pabyColorTable[i * 4 + 1], // Green
1715 9284 : poDS->pabyColorTable[i * 4 + 2], // Blue
1716 : 255 // Alpha
1717 9284 : };
1718 :
1719 9284 : poDS->poColorTable->SetColorEntry(i, &oEntry);
1720 : }
1721 : }
1722 68 : poDS->nBands = 1;
1723 68 : break;
1724 0 : default:
1725 0 : CPLError(CE_Warning, CPLE_IllegalArg,
1726 : "Invalid RSW bit depth %lu.",
1727 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1728 0 : delete poDS;
1729 0 : return nullptr;
1730 : }
1731 126 : eType = GDT_Byte;
1732 : }
1733 : else
1734 : {
1735 74 : poDS->nBands = 1;
1736 74 : if (poDS->sHeader.nBitDepth == 8)
1737 : {
1738 0 : eType = GDT_Byte;
1739 : }
1740 74 : else if (poDS->sHeader.nBitDepth == 16)
1741 : {
1742 0 : eType = GDT_Int16;
1743 : }
1744 74 : else if (poDS->sHeader.nBitDepth == 32)
1745 : {
1746 9 : eType = GDT_Int32;
1747 : }
1748 65 : else if (poDS->sHeader.nBitDepth == 64)
1749 : {
1750 65 : eType = GDT_Float64;
1751 : }
1752 : else
1753 : {
1754 0 : CPLError(CE_Warning, CPLE_IllegalArg, "Invalid MTW bit depth %lu.",
1755 0 : static_cast<unsigned long>(poDS->sHeader.nBitDepth));
1756 0 : delete poDS;
1757 0 : return nullptr;
1758 : }
1759 : }
1760 :
1761 200 : if (poDS->sHeader.nTileWidth == 0 || poDS->sHeader.nTileWidth > INT_MAX ||
1762 200 : poDS->sHeader.nTileHeight == 0 || poDS->sHeader.nTileHeight > INT_MAX)
1763 : {
1764 0 : CPLDebug("RMF", "Invalid tile dimension : %u x %u",
1765 : poDS->sHeader.nTileWidth, poDS->sHeader.nTileHeight);
1766 0 : delete poDS;
1767 0 : return nullptr;
1768 : }
1769 :
1770 200 : const int nDataSize = GDALGetDataTypeSizeBytes(eType);
1771 200 : const int nBlockXSize = static_cast<int>(poDS->sHeader.nTileWidth);
1772 200 : const int nBlockYSize = static_cast<int>(poDS->sHeader.nTileHeight);
1773 200 : if (nDataSize == 0 || nBlockXSize > INT_MAX / nBlockYSize ||
1774 200 : nBlockYSize > INT_MAX / nDataSize ||
1775 200 : nBlockXSize > INT_MAX / (nBlockYSize * nDataSize))
1776 : {
1777 0 : CPLDebug("RMF", "Too big raster / tile dimension");
1778 0 : delete poDS;
1779 0 : return nullptr;
1780 : }
1781 :
1782 200 : poDS->nXTiles = DIV_ROUND_UP(poDS->nRasterXSize, nBlockXSize);
1783 200 : poDS->nYTiles = DIV_ROUND_UP(poDS->nRasterYSize, nBlockYSize);
1784 :
1785 : #ifdef DEBUG
1786 200 : CPLDebug("RMF", "Image is %d tiles wide, %d tiles long", poDS->nXTiles,
1787 : poDS->nYTiles);
1788 : #endif
1789 :
1790 : /* -------------------------------------------------------------------- */
1791 : /* Choose compression scheme. */
1792 : /* -------------------------------------------------------------------- */
1793 200 : if (CE_None != poDS->SetupCompression(eType, poOpenInfo->pszFilename))
1794 : {
1795 0 : delete poDS;
1796 0 : return nullptr;
1797 : }
1798 :
1799 200 : if (poOpenInfo->eAccess == GA_Update)
1800 : {
1801 20 : if (poParentDS == nullptr)
1802 : {
1803 12 : if (CE_None !=
1804 12 : poDS->InitCompressorData(poOpenInfo->papszOpenOptions))
1805 : {
1806 0 : delete poDS;
1807 0 : return nullptr;
1808 : }
1809 : }
1810 : else
1811 : {
1812 8 : poDS->poCompressData = poParentDS->poCompressData;
1813 : }
1814 : }
1815 : /* -------------------------------------------------------------------- */
1816 : /* Create band information objects. */
1817 : /* -------------------------------------------------------------------- */
1818 516 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
1819 316 : poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
1820 :
1821 200 : poDS->SetupNBits();
1822 :
1823 200 : if (poDS->nBands > 1)
1824 : {
1825 58 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
1826 : }
1827 : /* -------------------------------------------------------------------- */
1828 : /* Set up projection. */
1829 : /* */
1830 : /* XXX: If projection value is not specified, but image still have */
1831 : /* georeferencing information, assume Gauss-Kruger projection. */
1832 : /* -------------------------------------------------------------------- */
1833 200 : if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE ||
1834 186 : poDS->sHeader.iProjection > 0 ||
1835 117 : (poDS->sHeader.dfPixelSize != 0.0 && poDS->sHeader.dfLLX != 0.0 &&
1836 15 : poDS->sHeader.dfLLY != 0.0))
1837 : {
1838 98 : GInt32 nProj =
1839 98 : (poDS->sHeader.iProjection) ? poDS->sHeader.iProjection : 1;
1840 98 : double padfPrjParams[8] = {poDS->sHeader.dfStdP1,
1841 98 : poDS->sHeader.dfStdP2,
1842 98 : poDS->sHeader.dfCenterLat,
1843 98 : poDS->sHeader.dfCenterLong,
1844 : 1.0,
1845 : 0.0,
1846 : 0.0,
1847 98 : 0.0};
1848 :
1849 : // XXX: Compute zone number for Gauss-Kruger (Transverse Mercator)
1850 : // projection if it is not specified.
1851 98 : if (nProj == 1L && poDS->sHeader.dfCenterLong == 0.0)
1852 : {
1853 6 : if (poDS->sExtHeader.nZone == 0)
1854 : {
1855 0 : double centerXCoord =
1856 0 : poDS->sHeader.dfLLX +
1857 0 : (poDS->nRasterXSize * poDS->sHeader.dfPixelSize / 2.0);
1858 0 : padfPrjParams[7] = floor((centerXCoord - 500000.0) / 1000000.0);
1859 : }
1860 : else
1861 : {
1862 6 : padfPrjParams[7] = poDS->sExtHeader.nZone;
1863 : }
1864 : }
1865 :
1866 98 : OGRErr res = OGRERR_FAILURE;
1867 98 : if (nProj >= 0 &&
1868 88 : (poDS->sExtHeader.nDatum >= 0 || poDS->sExtHeader.nEllipsoid >= 0))
1869 : {
1870 88 : res = poDS->m_oSRS.importFromPanorama(
1871 88 : nProj, poDS->sExtHeader.nDatum, poDS->sExtHeader.nEllipsoid,
1872 : padfPrjParams);
1873 : }
1874 :
1875 111 : if (poDS->sHeader.iEPSGCode > RMF_EPSG_MIN_CODE &&
1876 13 : (OGRERR_NONE != res || poDS->m_oSRS.IsLocal()))
1877 : {
1878 1 : res = poDS->m_oSRS.importFromEPSG(poDS->sHeader.iEPSGCode);
1879 : }
1880 :
1881 : const char *pszSetVertCS =
1882 98 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "RMF_SET_VERTCS",
1883 : CPLGetConfigOption("RMF_SET_VERTCS", "NO"));
1884 98 : if (CPLTestBool(pszSetVertCS) && res == OGRERR_NONE &&
1885 0 : poDS->sExtHeader.nVertDatum > 0)
1886 : {
1887 0 : poDS->m_oSRS.importVertCSFromPanorama(poDS->sExtHeader.nVertDatum);
1888 : }
1889 : }
1890 :
1891 : /* -------------------------------------------------------------------- */
1892 : /* Set up georeferencing. */
1893 : /* -------------------------------------------------------------------- */
1894 200 : if ((poDS->eRMFType == RMFT_RSW && poDS->sHeader.iGeorefFlag) ||
1895 128 : (poDS->eRMFType == RMFT_MTW && poDS->sHeader.dfPixelSize != 0.0))
1896 : {
1897 146 : poDS->m_gt[0] = poDS->sHeader.dfLLX;
1898 292 : poDS->m_gt[3] = poDS->sHeader.dfLLY +
1899 146 : poDS->nRasterYSize * poDS->sHeader.dfPixelSize;
1900 146 : poDS->m_gt[1] = poDS->sHeader.dfPixelSize;
1901 146 : poDS->m_gt[5] = -poDS->sHeader.dfPixelSize;
1902 146 : poDS->m_gt[2] = 0.0;
1903 146 : poDS->m_gt[4] = 0.0;
1904 : }
1905 :
1906 : /* -------------------------------------------------------------------- */
1907 : /* Set units. */
1908 : /* -------------------------------------------------------------------- */
1909 :
1910 200 : if (poDS->eRMFType == RMFT_MTW)
1911 : {
1912 74 : CPLFree(poDS->pszUnitType);
1913 74 : poDS->pszUnitType = RMFUnitTypeToStr(poDS->sHeader.iElevationUnit);
1914 : }
1915 :
1916 : /* -------------------------------------------------------------------- */
1917 : /* Report some other dataset related information. */
1918 : /* -------------------------------------------------------------------- */
1919 :
1920 200 : if (poDS->eRMFType == RMFT_MTW)
1921 : {
1922 74 : char szTemp[256] = {};
1923 :
1924 74 : snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[0]);
1925 74 : poDS->SetMetadataItem("ELEVATION_MINIMUM", szTemp);
1926 :
1927 74 : snprintf(szTemp, sizeof(szTemp), "%g", poDS->sHeader.adfElevMinMax[1]);
1928 74 : poDS->SetMetadataItem("ELEVATION_MAXIMUM", szTemp);
1929 :
1930 74 : poDS->SetMetadataItem("ELEVATION_UNITS", poDS->pszUnitType);
1931 :
1932 74 : snprintf(szTemp, sizeof(szTemp), "%d", poDS->sHeader.iElevationType);
1933 74 : poDS->SetMetadataItem("ELEVATION_TYPE", szTemp);
1934 : }
1935 :
1936 : /* -------------------------------------------------------------------- */
1937 : /* Check for overviews. */
1938 : /* -------------------------------------------------------------------- */
1939 200 : if (nNextHeaderOffset == 0 && poParentDS == nullptr)
1940 : {
1941 119 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
1942 : }
1943 :
1944 : /* Set frame */
1945 200 : if (poDS->sHeader.nROIOffset &&
1946 107 : poDS->sHeader.nROISize >= sizeof(RSWFrame) &&
1947 12 : poDS->sHeader.nROISize <= ROI_MAX_SIZE_TO_AVOID_EXCESSIVE_RAM_USAGE)
1948 : {
1949 : GByte *pabyROI = reinterpret_cast<GByte *>(
1950 12 : VSI_MALLOC_VERBOSE(poDS->sHeader.nROISize));
1951 12 : if (pabyROI == nullptr)
1952 : {
1953 0 : delete poDS;
1954 0 : return nullptr;
1955 : }
1956 :
1957 12 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nROIOffset),
1958 : SEEK_SET);
1959 12 : if (VSIFReadL(pabyROI, poDS->sHeader.nROISize, 1, poDS->fp) != 1)
1960 : {
1961 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read ROI");
1962 0 : CPLFree(pabyROI);
1963 0 : delete poDS;
1964 0 : return nullptr;
1965 : }
1966 :
1967 : GInt32 nFrameType;
1968 12 : RMF_READ_LONG(pabyROI, nFrameType, 0);
1969 12 : if (nFrameType == nPolygonType)
1970 : {
1971 24 : CPLString osWKT = "POLYGON((";
1972 12 : bool bFirst = true;
1973 :
1974 12 : CPLDebug("RMF", "ROI coordinates:");
1975 : /* coverity[tainted_data] */
1976 12 : for (GUInt32 i = sizeof(RSWFrame);
1977 1734 : i + sizeof(RSWFrameCoord) <= poDS->sHeader.nROISize;
1978 1722 : i += sizeof(RSWFrameCoord))
1979 : {
1980 : GInt32 nX, nY;
1981 1722 : RMF_READ_LONG(pabyROI, nX, i);
1982 1722 : RMF_READ_LONG(pabyROI, nY, i + 4);
1983 :
1984 1722 : CPLDebug("RMF", "X: %d, Y: %d", nX, nY);
1985 :
1986 : double dfX =
1987 1722 : poDS->m_gt[0] + nX * poDS->m_gt[1] + nY * poDS->m_gt[2];
1988 : double dfY =
1989 1722 : poDS->m_gt[3] + nX * poDS->m_gt[4] + nY * poDS->m_gt[5];
1990 :
1991 1722 : if (bFirst)
1992 : {
1993 12 : osWKT += CPLSPrintf("%f %f", dfX, dfY);
1994 12 : bFirst = false;
1995 : }
1996 : else
1997 : {
1998 1710 : osWKT += CPLSPrintf(", %f %f", dfX, dfY);
1999 : }
2000 : }
2001 12 : osWKT += "))";
2002 12 : CPLDebug("RMF", "Frame WKT: %s", osWKT.c_str());
2003 12 : poDS->SetMetadataItem(MD_FRAME_KEY, osWKT);
2004 : }
2005 12 : CPLFree(pabyROI);
2006 : }
2007 :
2008 : #undef RMF_READ_DOUBLE
2009 : #undef RMF_READ_LONG
2010 : #undef RMF_READ_ULONG
2011 :
2012 200 : if (poDS->sHeader.nFlagsTblOffset && poDS->sHeader.nFlagsTblSize)
2013 : {
2014 107 : VSIFSeekL(poDS->fp, poDS->GetFileOffset(poDS->sHeader.nFlagsTblOffset),
2015 : SEEK_SET);
2016 107 : CPLDebug("RMF", "Blocks flags:");
2017 : /* coverity[tainted_data] */
2018 416 : for (GUInt32 i = 0; i < poDS->sHeader.nFlagsTblSize; i += sizeof(GByte))
2019 : {
2020 : GByte nValue;
2021 309 : if (VSIFReadL(&nValue, 1, sizeof(nValue), poDS->fp) !=
2022 : sizeof(nValue))
2023 : {
2024 0 : CPLDebug("RMF", "Cannot read Block flag at index %u", i);
2025 0 : break;
2026 : }
2027 309 : CPLDebug("RMF", "Block %u -- flag %d", i, nValue);
2028 : }
2029 : }
2030 200 : return poDS;
2031 : }
2032 :
2033 : /************************************************************************/
2034 : /* Create() */
2035 : /************************************************************************/
2036 89 : GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
2037 : int nBandsIn, GDALDataType eType,
2038 : char **papszParamList)
2039 : {
2040 89 : return Create(pszFilename, nXSize, nYSize, nBandsIn, eType, papszParamList,
2041 89 : nullptr, 1.0);
2042 : }
2043 :
2044 123 : GDALDataset *RMFDataset::Create(const char *pszFilename, int nXSize, int nYSize,
2045 : int nBandsIn, GDALDataType eType,
2046 : char **papszParamList, RMFDataset *poParentDS,
2047 : double dfOvFactor)
2048 :
2049 : {
2050 123 : if (nBandsIn != 1 && nBandsIn != 3)
2051 : {
2052 7 : CPLError(CE_Failure, CPLE_NotSupported,
2053 : "RMF driver doesn't support %d bands. Must be 1 or 3.",
2054 : nBandsIn);
2055 :
2056 7 : return nullptr;
2057 : }
2058 :
2059 116 : if (nBandsIn == 1 && eType != GDT_Byte && eType != GDT_Int16 &&
2060 52 : eType != GDT_Int32 && eType != GDT_Float64)
2061 : {
2062 17 : CPLError(
2063 : CE_Failure, CPLE_AppDefined,
2064 : "Attempt to create RMF dataset with an illegal data type (%s), "
2065 : "only Byte, Int16, Int32 and Float64 types supported "
2066 : "by the format for single-band images.",
2067 : GDALGetDataTypeName(eType));
2068 :
2069 17 : return nullptr;
2070 : }
2071 :
2072 99 : if (nBandsIn == 3 && eType != GDT_Byte)
2073 : {
2074 13 : CPLError(
2075 : CE_Failure, CPLE_AppDefined,
2076 : "Attempt to create RMF dataset with an illegal data type (%s), "
2077 : "only Byte type supported by the format for three-band images.",
2078 : GDALGetDataTypeName(eType));
2079 :
2080 13 : return nullptr;
2081 : }
2082 :
2083 : /* -------------------------------------------------------------------- */
2084 : /* Create the dataset. */
2085 : /* -------------------------------------------------------------------- */
2086 86 : RMFDataset *poDS = new RMFDataset();
2087 :
2088 86 : GUInt32 nBlockXSize =
2089 86 : (nXSize < RMF_DEFAULT_BLOCKXSIZE) ? nXSize : RMF_DEFAULT_BLOCKXSIZE;
2090 86 : GUInt32 nBlockYSize =
2091 86 : (nYSize < RMF_DEFAULT_BLOCKYSIZE) ? nYSize : RMF_DEFAULT_BLOCKYSIZE;
2092 : double dfScale;
2093 : double dfResolution;
2094 : double dfPixelSize;
2095 86 : if (poParentDS == nullptr)
2096 : {
2097 52 : poDS->fp = VSIFOpenL(pszFilename, "w+b");
2098 52 : if (poDS->fp == nullptr)
2099 : {
2100 3 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create file %s.",
2101 : pszFilename);
2102 3 : delete poDS;
2103 3 : return nullptr;
2104 : }
2105 :
2106 : const char *pszScaleValue =
2107 49 : CSLFetchNameValue(papszParamList, MD_SCALE_KEY);
2108 49 : if (pszScaleValue != nullptr && CPLStrnlen(pszScaleValue, 10) > 4)
2109 : {
2110 0 : dfScale = atof(pszScaleValue + 4);
2111 : }
2112 : else
2113 : {
2114 49 : dfScale = RMF_DEFAULT_SCALE;
2115 : }
2116 49 : dfResolution = RMF_DEFAULT_RESOLUTION;
2117 49 : dfPixelSize = 1;
2118 :
2119 49 : if (CPLFetchBool(papszParamList, "MTW", false))
2120 12 : poDS->eRMFType = RMFT_MTW;
2121 : else
2122 37 : poDS->eRMFType = RMFT_RSW;
2123 :
2124 49 : GUInt32 iVersion = RMF_VERSION;
2125 49 : const char *pszRMFHUGE = CSLFetchNameValue(papszParamList, "RMFHUGE");
2126 :
2127 49 : if (pszRMFHUGE == nullptr)
2128 35 : pszRMFHUGE = "NO"; // Keep old behavior by default
2129 :
2130 49 : if (EQUAL(pszRMFHUGE, "NO"))
2131 : {
2132 42 : iVersion = RMF_VERSION;
2133 : }
2134 7 : else if (EQUAL(pszRMFHUGE, "YES"))
2135 : {
2136 7 : iVersion = RMF_VERSION_HUGE;
2137 : }
2138 0 : else if (EQUAL(pszRMFHUGE, "IF_SAFER"))
2139 : {
2140 : const double dfImageSize =
2141 0 : static_cast<double>(nXSize) * static_cast<double>(nYSize) *
2142 0 : static_cast<double>(nBandsIn) *
2143 0 : static_cast<double>(GDALGetDataTypeSizeBytes(eType));
2144 0 : if (dfImageSize > 3.0 * 1024.0 * 1024.0 * 1024.0)
2145 : {
2146 0 : iVersion = RMF_VERSION_HUGE;
2147 : }
2148 : else
2149 : {
2150 0 : iVersion = RMF_VERSION;
2151 : }
2152 : }
2153 :
2154 49 : const char *pszValue = CSLFetchNameValue(papszParamList, "BLOCKXSIZE");
2155 49 : if (pszValue != nullptr)
2156 1 : nBlockXSize = atoi(pszValue);
2157 49 : if (static_cast<int>(nBlockXSize) <= 0)
2158 0 : nBlockXSize = RMF_DEFAULT_BLOCKXSIZE;
2159 :
2160 49 : pszValue = CSLFetchNameValue(papszParamList, "BLOCKYSIZE");
2161 49 : if (pszValue != nullptr)
2162 1 : nBlockYSize = atoi(pszValue);
2163 49 : if (static_cast<int>(nBlockYSize) <= 0)
2164 0 : nBlockYSize = RMF_DEFAULT_BLOCKXSIZE;
2165 :
2166 49 : if (poDS->eRMFType == RMFT_MTW)
2167 12 : memcpy(poDS->sHeader.bySignature, RMF_SigMTW, RMF_SIGNATURE_SIZE);
2168 : else
2169 37 : memcpy(poDS->sHeader.bySignature, RMF_SigRSW, RMF_SIGNATURE_SIZE);
2170 49 : poDS->sHeader.iVersion = iVersion;
2171 49 : poDS->sHeader.nOvrOffset = 0x00;
2172 : }
2173 : else
2174 : {
2175 34 : poDS->fp = poParentDS->fp;
2176 34 : memcpy(poDS->sHeader.bySignature, poParentDS->sHeader.bySignature,
2177 : RMF_SIGNATURE_SIZE);
2178 34 : poDS->sHeader.iVersion = poParentDS->sHeader.iVersion;
2179 34 : poDS->eRMFType = poParentDS->eRMFType;
2180 34 : nBlockXSize = poParentDS->sHeader.nTileWidth;
2181 34 : nBlockYSize = poParentDS->sHeader.nTileHeight;
2182 34 : dfScale = poParentDS->sHeader.dfScale;
2183 34 : dfResolution = poParentDS->sHeader.dfResolution / dfOvFactor;
2184 34 : dfPixelSize = poParentDS->sHeader.dfPixelSize * dfOvFactor;
2185 :
2186 34 : poDS->nHeaderOffset = poParentDS->GetLastOffset();
2187 34 : poParentDS->sHeader.nOvrOffset =
2188 34 : poDS->GetRMFOffset(poDS->nHeaderOffset, &poDS->nHeaderOffset);
2189 34 : poParentDS->bHeaderDirty = true;
2190 34 : VSIFSeekL(poDS->fp, poDS->nHeaderOffset, SEEK_SET);
2191 34 : poDS->poParentDS = poParentDS;
2192 34 : CPLDebug("RMF",
2193 : "Create overview subfile at " CPL_FRMT_GUIB
2194 : " with size %dx%d, parent overview offset %d",
2195 : poDS->nHeaderOffset, nXSize, nYSize,
2196 : poParentDS->sHeader.nOvrOffset);
2197 : }
2198 : /* -------------------------------------------------------------------- */
2199 : /* Fill the RMFHeader */
2200 : /* -------------------------------------------------------------------- */
2201 83 : CPLDebug("RMF", "Version %d", poDS->sHeader.iVersion);
2202 :
2203 83 : poDS->sHeader.iUserID = 0x00;
2204 83 : memset(poDS->sHeader.byName, 0, sizeof(poDS->sHeader.byName));
2205 83 : poDS->sHeader.nBitDepth = GDALGetDataTypeSizeBits(eType) * nBandsIn;
2206 83 : poDS->sHeader.nHeight = nYSize;
2207 83 : poDS->sHeader.nWidth = nXSize;
2208 83 : poDS->sHeader.nTileWidth = nBlockXSize;
2209 83 : poDS->sHeader.nTileHeight = nBlockYSize;
2210 :
2211 83 : poDS->nXTiles = poDS->sHeader.nXTiles =
2212 83 : DIV_ROUND_UP(nXSize, poDS->sHeader.nTileWidth);
2213 83 : poDS->nYTiles = poDS->sHeader.nYTiles =
2214 83 : DIV_ROUND_UP(nYSize, poDS->sHeader.nTileHeight);
2215 83 : poDS->sHeader.nLastTileHeight = nYSize % poDS->sHeader.nTileHeight;
2216 83 : if (!poDS->sHeader.nLastTileHeight)
2217 44 : poDS->sHeader.nLastTileHeight = poDS->sHeader.nTileHeight;
2218 83 : poDS->sHeader.nLastTileWidth = nXSize % poDS->sHeader.nTileWidth;
2219 83 : if (!poDS->sHeader.nLastTileWidth)
2220 40 : poDS->sHeader.nLastTileWidth = poDS->sHeader.nTileWidth;
2221 :
2222 : // poDS->sHeader.nROIOffset = 0x00;
2223 : // poDS->sHeader.nROISize = 0x00;
2224 :
2225 83 : vsi_l_offset nCurPtr = poDS->nHeaderOffset + RMF_HEADER_SIZE;
2226 :
2227 : // Extended header
2228 83 : poDS->sHeader.nExtHdrOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2229 83 : poDS->sHeader.nExtHdrSize = RMF_EXT_HEADER_SIZE;
2230 83 : nCurPtr += poDS->sHeader.nExtHdrSize;
2231 :
2232 : // Color table
2233 83 : if (poDS->eRMFType == RMFT_RSW && nBandsIn == 1)
2234 : {
2235 34 : if (poDS->sHeader.nBitDepth > 8)
2236 : {
2237 6 : CPLError(CE_Failure, CPLE_AppDefined,
2238 : "Cannot create color table of RSW with nBitDepth = %d. "
2239 : "Retry with MTW ?",
2240 : poDS->sHeader.nBitDepth);
2241 6 : delete poDS;
2242 6 : return nullptr;
2243 : }
2244 :
2245 28 : poDS->sHeader.nClrTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2246 28 : poDS->nColorTableSize = 1 << poDS->sHeader.nBitDepth;
2247 28 : poDS->sHeader.nClrTblSize = poDS->nColorTableSize * 4;
2248 28 : poDS->pabyColorTable =
2249 28 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(poDS->sHeader.nClrTblSize));
2250 28 : if (poDS->pabyColorTable == nullptr)
2251 : {
2252 0 : delete poDS;
2253 0 : return nullptr;
2254 : }
2255 7196 : for (GUInt32 i = 0; i < poDS->nColorTableSize; i++)
2256 : {
2257 7168 : poDS->pabyColorTable[i * 4 + 0] = static_cast<GByte>(i);
2258 7168 : poDS->pabyColorTable[i * 4 + 1] = static_cast<GByte>(i);
2259 7168 : poDS->pabyColorTable[i * 4 + 2] = static_cast<GByte>(i);
2260 7168 : poDS->pabyColorTable[i * 4 + 3] = 0;
2261 : }
2262 28 : nCurPtr += poDS->sHeader.nClrTblSize;
2263 : }
2264 : else
2265 : {
2266 49 : poDS->sHeader.nClrTblOffset = 0x00;
2267 49 : poDS->sHeader.nClrTblSize = 0x00;
2268 : }
2269 :
2270 : // Add room for ROI (frame)
2271 77 : poDS->sHeader.nROIOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2272 77 : poDS->sHeader.nROISize = 0x00;
2273 77 : nCurPtr +=
2274 : sizeof(RSWFrame) +
2275 : sizeof(RSWFrameCoord) *
2276 : nMaxFramePointCount; // Allocate nMaxFramePointCount coordinates for frame
2277 :
2278 : // Add blocks flags
2279 77 : poDS->sHeader.nFlagsTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2280 77 : poDS->sHeader.nFlagsTblSize =
2281 77 : sizeof(GByte) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
2282 77 : nCurPtr += poDS->sHeader.nFlagsTblSize;
2283 :
2284 : // Blocks table
2285 77 : poDS->sHeader.nTileTblOffset = poDS->GetRMFOffset(nCurPtr, &nCurPtr);
2286 77 : poDS->sHeader.nTileTblSize =
2287 77 : 2 * sizeof(GUInt32) * poDS->sHeader.nXTiles * poDS->sHeader.nYTiles;
2288 77 : poDS->paiTiles =
2289 77 : static_cast<GUInt32 *>(CPLCalloc(poDS->sHeader.nTileTblSize, 1));
2290 : // nCurPtr += poDS->sHeader.nTileTblSize;
2291 77 : const GUInt32 nTileSize = poDS->sHeader.nTileWidth *
2292 77 : poDS->sHeader.nTileHeight *
2293 77 : GDALGetDataTypeSizeBytes(eType);
2294 77 : poDS->sHeader.nSize =
2295 77 : poDS->paiTiles[poDS->sHeader.nTileTblSize / 4 - 2] + nTileSize;
2296 :
2297 : // Elevation units
2298 77 : poDS->sHeader.iElevationUnit = RMFStrToUnitType(poDS->pszUnitType);
2299 :
2300 77 : poDS->sHeader.iMapType = -1;
2301 77 : poDS->sHeader.iProjection = -1;
2302 77 : poDS->sHeader.iEPSGCode = -1;
2303 77 : poDS->sHeader.dfScale = dfScale;
2304 77 : poDS->sHeader.dfResolution = dfResolution;
2305 77 : poDS->sHeader.dfPixelSize = dfPixelSize;
2306 77 : poDS->sHeader.iMaskType = 0;
2307 77 : poDS->sHeader.iMaskStep = 0;
2308 77 : poDS->sHeader.iFrameFlag = 1; // 1 - Frame not using
2309 : // poDS->sHeader.nFlagsTblOffset = 0x00;
2310 : // poDS->sHeader.nFlagsTblSize = 0x00;
2311 77 : poDS->sHeader.nFileSize0 = 0x00;
2312 77 : poDS->sHeader.nFileSize1 = 0x00;
2313 77 : poDS->sHeader.iUnknown = 0;
2314 77 : poDS->sHeader.iGeorefFlag = 0;
2315 77 : poDS->sHeader.iInverse = 0;
2316 77 : poDS->sHeader.iJpegQuality = 0;
2317 77 : memset(poDS->sHeader.abyInvisibleColors, 0,
2318 : sizeof(poDS->sHeader.abyInvisibleColors));
2319 77 : poDS->sHeader.iElevationType = 0;
2320 :
2321 77 : poDS->nRasterXSize = nXSize;
2322 77 : poDS->nRasterYSize = nYSize;
2323 77 : poDS->eAccess = GA_Update;
2324 77 : poDS->nBands = nBandsIn;
2325 :
2326 77 : if (poParentDS == nullptr)
2327 : {
2328 43 : poDS->sHeader.adfElevMinMax[0] = 0.0;
2329 43 : poDS->sHeader.adfElevMinMax[1] = 0.0;
2330 43 : poDS->sHeader.dfNoData = 0.0;
2331 43 : poDS->sHeader.iCompression =
2332 43 : GetCompressionType(CSLFetchNameValue(papszParamList, "COMPRESS"));
2333 43 : if (CE_None != poDS->InitCompressorData(papszParamList))
2334 : {
2335 0 : delete poDS;
2336 0 : return nullptr;
2337 : }
2338 :
2339 43 : if (poDS->sHeader.iCompression == RMF_COMPRESSION_JPEG)
2340 : {
2341 : const char *pszJpegQuality =
2342 1 : CSLFetchNameValue(papszParamList, "JPEG_QUALITY");
2343 1 : if (pszJpegQuality == nullptr)
2344 : {
2345 1 : poDS->sHeader.iJpegQuality = 75;
2346 : }
2347 : else
2348 : {
2349 0 : int iJpegQuality = atoi(pszJpegQuality);
2350 0 : if (iJpegQuality < 10 || iJpegQuality > 100)
2351 : {
2352 0 : CPLError(CE_Failure, CPLE_IllegalArg,
2353 : "JPEG_QUALITY=%s is not a legal value in the "
2354 : "range 10-100.\n"
2355 : "Defaulting to 75",
2356 : pszJpegQuality);
2357 0 : iJpegQuality = 75;
2358 : }
2359 0 : poDS->sHeader.iJpegQuality = static_cast<GByte>(iJpegQuality);
2360 : }
2361 : }
2362 :
2363 43 : if (CE_None != poDS->SetupCompression(eType, pszFilename))
2364 : {
2365 0 : delete poDS;
2366 0 : return nullptr;
2367 : }
2368 : }
2369 : else
2370 : {
2371 34 : poDS->sHeader.adfElevMinMax[0] = poParentDS->sHeader.adfElevMinMax[0];
2372 34 : poDS->sHeader.adfElevMinMax[1] = poParentDS->sHeader.adfElevMinMax[1];
2373 34 : poDS->sHeader.dfNoData = poParentDS->sHeader.dfNoData;
2374 34 : poDS->sHeader.iCompression = poParentDS->sHeader.iCompression;
2375 34 : poDS->sHeader.iJpegQuality = poParentDS->sHeader.iJpegQuality;
2376 34 : poDS->Decompress = poParentDS->Decompress;
2377 34 : poDS->Compress = poParentDS->Compress;
2378 34 : poDS->poCompressData = poParentDS->poCompressData;
2379 : }
2380 :
2381 77 : if (nBandsIn > 1)
2382 : {
2383 13 : poDS->SetMetadataItem("INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE");
2384 : }
2385 :
2386 77 : poDS->WriteHeader();
2387 :
2388 : /* -------------------------------------------------------------------- */
2389 : /* Create band information objects. */
2390 : /* -------------------------------------------------------------------- */
2391 180 : for (int iBand = 1; iBand <= poDS->nBands; iBand++)
2392 103 : poDS->SetBand(iBand, new RMFRasterBand(poDS, iBand, eType));
2393 :
2394 77 : poDS->SetupNBits();
2395 :
2396 77 : return GDALDataset::FromHandle(poDS);
2397 : }
2398 :
2399 : // GIS Panorama 11 was introduced new format for huge files (greater than 3 Gb)
2400 3859 : vsi_l_offset RMFDataset::GetFileOffset(GUInt32 iRMFOffset) const
2401 : {
2402 3859 : if (sHeader.iVersion >= RMF_VERSION_HUGE)
2403 : {
2404 1142 : return (static_cast<vsi_l_offset>(iRMFOffset)) * RMF_HUGE_OFFSET_FACTOR;
2405 : }
2406 :
2407 2717 : return static_cast<vsi_l_offset>(iRMFOffset);
2408 : }
2409 :
2410 966 : GUInt32 RMFDataset::GetRMFOffset(vsi_l_offset nFileOffset,
2411 : vsi_l_offset *pnNewFileOffset) const
2412 : {
2413 966 : if (sHeader.iVersion >= RMF_VERSION_HUGE)
2414 : {
2415 : // Round offset to next RMF_HUGE_OFFSET_FACTOR
2416 272 : const GUInt32 iRMFOffset =
2417 272 : static_cast<GUInt32>((nFileOffset + (RMF_HUGE_OFFSET_FACTOR - 1)) /
2418 : RMF_HUGE_OFFSET_FACTOR);
2419 272 : if (pnNewFileOffset != nullptr)
2420 : {
2421 205 : *pnNewFileOffset = GetFileOffset(iRMFOffset);
2422 : }
2423 272 : return iRMFOffset;
2424 : }
2425 :
2426 694 : if (pnNewFileOffset != nullptr)
2427 : {
2428 561 : *pnNewFileOffset = nFileOffset;
2429 : }
2430 694 : return static_cast<GUInt32>(nFileOffset);
2431 : }
2432 :
2433 200 : RMFDataset *RMFDataset::OpenOverview(RMFDataset *poParent,
2434 : GDALOpenInfo *poOpenInfo)
2435 : {
2436 200 : if (sHeader.nOvrOffset == 0)
2437 : {
2438 111 : return nullptr;
2439 : }
2440 :
2441 89 : if (poParent == nullptr)
2442 : {
2443 0 : return nullptr;
2444 : }
2445 :
2446 89 : vsi_l_offset nSubOffset = GetFileOffset(sHeader.nOvrOffset);
2447 :
2448 89 : CPLDebug("RMF",
2449 : "Try to open overview subfile at " CPL_FRMT_GUIB " for '%s'",
2450 : nSubOffset, poOpenInfo->pszFilename);
2451 :
2452 89 : if (!poParent->poOvrDatasets.empty())
2453 : {
2454 49 : if (poParent->GetFileOffset(poParent->sHeader.nOvrOffset) == nSubOffset)
2455 : {
2456 2 : CPLError(CE_Warning, CPLE_IllegalArg,
2457 : "Recursive subdataset list is detected. "
2458 : "Overview open failed.");
2459 2 : return nullptr;
2460 : }
2461 :
2462 58 : for (size_t n = 0; n != poParent->poOvrDatasets.size() - 1; ++n)
2463 : {
2464 13 : RMFDataset *poOvr(poParent->poOvrDatasets[n]);
2465 :
2466 13 : if (poOvr == nullptr)
2467 0 : continue;
2468 13 : if (poOvr->GetFileOffset(poOvr->sHeader.nOvrOffset) == nSubOffset)
2469 : {
2470 2 : CPLError(CE_Warning, CPLE_IllegalArg,
2471 : "Recursive subdataset list is detected. "
2472 : "Overview open failed.");
2473 2 : return nullptr;
2474 : }
2475 : }
2476 : }
2477 :
2478 85 : size_t nHeaderSize(RMF_HEADER_SIZE);
2479 : GByte *pabyNewHeader;
2480 : pabyNewHeader = static_cast<GByte *>(
2481 85 : CPLRealloc(poOpenInfo->pabyHeader, nHeaderSize + 1));
2482 85 : if (pabyNewHeader == nullptr)
2483 : {
2484 0 : CPLError(CE_Warning, CPLE_OutOfMemory,
2485 : "Can't allocate buffer for overview header");
2486 0 : return nullptr;
2487 : }
2488 :
2489 85 : poOpenInfo->pabyHeader = pabyNewHeader;
2490 85 : memset(poOpenInfo->pabyHeader, 0, nHeaderSize + 1);
2491 85 : VSIFSeekL(fp, nSubOffset, SEEK_SET);
2492 85 : poOpenInfo->nHeaderBytes =
2493 85 : static_cast<int>(VSIFReadL(poOpenInfo->pabyHeader, 1, nHeaderSize, fp));
2494 :
2495 85 : return Open(poOpenInfo, poParent, nSubOffset);
2496 : }
2497 :
2498 17 : CPLErr RMFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
2499 : const int *panOverviewList, int nBandsIn,
2500 : const int *panBandList,
2501 : GDALProgressFunc pfnProgress,
2502 : void *pProgressData,
2503 : CSLConstList papszOptions)
2504 : {
2505 17 : bool bUseGenericHandling = false;
2506 :
2507 17 : if (GetAccess() != GA_Update)
2508 : {
2509 0 : CPLDebug("RMF", "File open for read-only accessing, "
2510 : "creating overviews externally.");
2511 :
2512 0 : bUseGenericHandling = true;
2513 : }
2514 :
2515 17 : if (bUseGenericHandling)
2516 : {
2517 0 : if (!poOvrDatasets.empty())
2518 : {
2519 0 : CPLError(CE_Failure, CPLE_NotSupported,
2520 : "Cannot add external overviews when there are already "
2521 : "internal overviews");
2522 0 : return CE_Failure;
2523 : }
2524 :
2525 0 : return GDALDataset::IBuildOverviews(
2526 : pszResampling, nOverviews, panOverviewList, nBandsIn, panBandList,
2527 0 : pfnProgress, pProgressData, papszOptions);
2528 : }
2529 :
2530 17 : if (nBandsIn != GetRasterCount())
2531 : {
2532 0 : CPLError(CE_Failure, CPLE_NotSupported,
2533 : "Generation of overviews in RMF is only "
2534 : "supported when operating on all bands. "
2535 : "Operation failed.");
2536 0 : return CE_Failure;
2537 : }
2538 :
2539 17 : if (nOverviews == 0)
2540 : {
2541 0 : if (poOvrDatasets.empty())
2542 : {
2543 0 : return GDALDataset::IBuildOverviews(
2544 : pszResampling, nOverviews, panOverviewList, nBandsIn,
2545 0 : panBandList, pfnProgress, pProgressData, papszOptions);
2546 : }
2547 0 : return CleanOverviews();
2548 : }
2549 :
2550 : // First destroy old overviews
2551 17 : if (CE_None != CleanOverviews())
2552 : {
2553 0 : return CE_Failure;
2554 : }
2555 :
2556 17 : CPLDebug("RMF", "Build overviews on dataset %d x %d size", GetRasterXSize(),
2557 : GetRasterYSize());
2558 :
2559 17 : GDALDataType eMainType = GetRasterBand(1)->GetRasterDataType();
2560 17 : RMFDataset *poParent = this;
2561 17 : double prevOvLevel = 1.0;
2562 51 : for (int n = 0; n != nOverviews; ++n)
2563 : {
2564 34 : int nOvLevel = panOverviewList[n];
2565 34 : const int nOXSize = DIV_ROUND_UP(GetRasterXSize(), nOvLevel);
2566 34 : const int nOYSize = DIV_ROUND_UP(GetRasterYSize(), nOvLevel);
2567 34 : CPLDebug("RMF", "\tCreate overview #%d size %d x %d", nOvLevel, nOXSize,
2568 : nOYSize);
2569 :
2570 : RMFDataset *poOvrDataset;
2571 34 : poOvrDataset = static_cast<RMFDataset *>(RMFDataset::Create(
2572 : nullptr, nOXSize, nOYSize, GetRasterCount(), eMainType, nullptr,
2573 : poParent, nOvLevel / prevOvLevel));
2574 :
2575 34 : if (poOvrDataset == nullptr)
2576 : {
2577 0 : CPLError(CE_Failure, CPLE_AppDefined,
2578 : "Can't create overview dataset #%d size %d x %d", nOvLevel,
2579 : nOXSize, nOYSize);
2580 0 : return CE_Failure;
2581 : }
2582 :
2583 34 : prevOvLevel = nOvLevel;
2584 34 : poParent = poOvrDataset;
2585 34 : poOvrDatasets.push_back(poOvrDataset);
2586 : }
2587 :
2588 : GDALRasterBand ***papapoOverviewBands =
2589 17 : static_cast<GDALRasterBand ***>(CPLCalloc(sizeof(void *), nBandsIn));
2590 : GDALRasterBand **papoBandList =
2591 17 : static_cast<GDALRasterBand **>(CPLCalloc(sizeof(void *), nBandsIn));
2592 :
2593 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2594 : {
2595 19 : GDALRasterBand *poBand = GetRasterBand(panBandList[iBand]);
2596 :
2597 19 : papoBandList[iBand] = poBand;
2598 38 : papapoOverviewBands[iBand] = static_cast<GDALRasterBand **>(
2599 19 : CPLCalloc(sizeof(void *), poBand->GetOverviewCount()));
2600 :
2601 57 : for (int i = 0; i < nOverviews; ++i)
2602 : {
2603 38 : papapoOverviewBands[iBand][i] = poBand->GetOverview(i);
2604 : }
2605 : }
2606 : #ifdef DEBUG
2607 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2608 : {
2609 19 : CPLDebug("RMF", "Try to create overview for #%d size %d x %d",
2610 19 : iBand + 1, papoBandList[iBand]->GetXSize(),
2611 19 : papoBandList[iBand]->GetYSize());
2612 57 : for (int i = 0; i < nOverviews; ++i)
2613 : {
2614 38 : CPLDebug("RMF", "\t%d x %d",
2615 38 : papapoOverviewBands[iBand][i]->GetXSize(),
2616 38 : papapoOverviewBands[iBand][i]->GetYSize());
2617 : }
2618 : }
2619 : #endif // DEBUG
2620 : CPLErr res;
2621 17 : res = GDALRegenerateOverviewsMultiBand(
2622 : nBandsIn, papoBandList, nOverviews, papapoOverviewBands, pszResampling,
2623 : pfnProgress, pProgressData, papszOptions);
2624 :
2625 36 : for (int iBand = 0; iBand < nBandsIn; ++iBand)
2626 : {
2627 19 : CPLFree(papapoOverviewBands[iBand]);
2628 : }
2629 :
2630 17 : CPLFree(papapoOverviewBands);
2631 17 : CPLFree(papoBandList);
2632 :
2633 17 : return res;
2634 : }
2635 :
2636 68 : CPLErr RMFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2637 : int nXSize, int nYSize, void *pData, int nBufXSize,
2638 : int nBufYSize, GDALDataType eBufType,
2639 : int nBandCount, BANDMAP_TYPE panBandMap,
2640 : GSpacing nPixelSpace, GSpacing nLineSpace,
2641 : GSpacing nBandSpace,
2642 : GDALRasterIOExtraArg *psExtraArg)
2643 : {
2644 : #ifdef DEBUG
2645 68 : CPLDebug("RMF", "Dataset %p, %s %d %d %d %d, %d %d", this,
2646 : (eRWFlag == GF_Read ? "Read" : "Write"), nXOff, nYOff, nXSize,
2647 : nYSize, nBufXSize, nBufYSize);
2648 : #endif // DEBUG
2649 68 : if (eRWFlag == GF_Read && poCompressData != nullptr &&
2650 0 : poCompressData->oThreadPool.GetThreadCount() > 0)
2651 : {
2652 0 : poCompressData->oThreadPool.WaitCompletion();
2653 : }
2654 :
2655 68 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
2656 : nBufXSize, nBufYSize, eBufType, nBandCount,
2657 : panBandMap, nPixelSpace, nLineSpace,
2658 68 : nBandSpace, psExtraArg);
2659 : }
2660 :
2661 238 : vsi_l_offset RMFDataset::GetLastOffset() const
2662 : {
2663 238 : vsi_l_offset nLastTileOff = 0;
2664 238 : GUInt32 nTiles(sHeader.nTileTblSize / sizeof(GUInt32));
2665 :
2666 768 : for (GUInt32 n = 0; n < nTiles; n += 2)
2667 : {
2668 530 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[n]);
2669 530 : GUInt32 nTileBytes = paiTiles[n + 1];
2670 530 : nLastTileOff = std::max(nLastTileOff, nTileOffset + nTileBytes);
2671 : }
2672 :
2673 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nROIOffset) +
2674 238 : sHeader.nROISize);
2675 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nClrTblOffset) +
2676 238 : sHeader.nClrTblSize);
2677 238 : nLastTileOff =
2678 238 : std::max(nLastTileOff,
2679 238 : GetFileOffset(sHeader.nTileTblOffset) + sHeader.nTileTblSize);
2680 238 : nLastTileOff =
2681 238 : std::max(nLastTileOff, GetFileOffset(sHeader.nFlagsTblOffset) +
2682 238 : sHeader.nFlagsTblSize);
2683 238 : nLastTileOff = std::max(nLastTileOff, GetFileOffset(sHeader.nExtHdrOffset) +
2684 238 : sHeader.nExtHdrSize);
2685 238 : return nLastTileOff;
2686 : }
2687 :
2688 17 : CPLErr RMFDataset::CleanOverviews()
2689 : {
2690 17 : if (sHeader.nOvrOffset == 0)
2691 : {
2692 13 : return CE_None;
2693 : }
2694 :
2695 4 : if (GetAccess() != GA_Update)
2696 : {
2697 0 : CPLError(CE_Failure, CPLE_NotSupported,
2698 : "File open for read-only accessing, "
2699 : "overviews cleanup failed.");
2700 0 : return CE_Failure;
2701 : }
2702 :
2703 4 : if (poParentDS != nullptr)
2704 : {
2705 0 : CPLError(CE_Failure, CPLE_NotSupported,
2706 : "Overviews cleanup for non-root dataset is not possible.");
2707 0 : return CE_Failure;
2708 : }
2709 :
2710 12 : for (size_t n = 0; n != poOvrDatasets.size(); ++n)
2711 : {
2712 8 : GDALClose(poOvrDatasets[n]);
2713 : }
2714 4 : poOvrDatasets.clear();
2715 :
2716 4 : vsi_l_offset nLastTileOff = GetLastOffset();
2717 :
2718 4 : if (0 != VSIFSeekL(fp, 0, SEEK_END))
2719 : {
2720 0 : CPLError(CE_Failure, CPLE_FileIO,
2721 : "Failed to seek to end of file, "
2722 : "overviews cleanup failed.");
2723 : }
2724 :
2725 4 : vsi_l_offset nFileSize = VSIFTellL(fp);
2726 4 : if (nFileSize < nLastTileOff)
2727 : {
2728 0 : CPLError(CE_Failure, CPLE_FileIO,
2729 : "Invalid file offset, "
2730 : "overviews cleanup failed.");
2731 0 : return CE_Failure;
2732 : }
2733 :
2734 4 : CPLDebug("RMF", "Truncate to " CPL_FRMT_GUIB, nLastTileOff);
2735 4 : CPLDebug("RMF", "File size: " CPL_FRMT_GUIB, nFileSize);
2736 :
2737 4 : if (0 != VSIFTruncateL(fp, nLastTileOff))
2738 : {
2739 0 : CPLError(CE_Failure, CPLE_FileIO,
2740 : "Failed to truncate file, "
2741 : "overviews cleanup failed.");
2742 0 : return CE_Failure;
2743 : }
2744 :
2745 4 : sHeader.nOvrOffset = 0;
2746 4 : bHeaderDirty = true;
2747 :
2748 4 : return CE_None;
2749 : }
2750 :
2751 : /************************************************************************/
2752 : /* GetCompressionType() */
2753 : /************************************************************************/
2754 :
2755 43 : GByte RMFDataset::GetCompressionType(const char *pszCompressName)
2756 : {
2757 43 : if (pszCompressName == nullptr || EQUAL(pszCompressName, "NONE"))
2758 : {
2759 35 : return RMF_COMPRESSION_NONE;
2760 : }
2761 8 : else if (EQUAL(pszCompressName, "LZW"))
2762 : {
2763 5 : return RMF_COMPRESSION_LZW;
2764 : }
2765 3 : else if (EQUAL(pszCompressName, "JPEG"))
2766 : {
2767 1 : return RMF_COMPRESSION_JPEG;
2768 : }
2769 2 : else if (EQUAL(pszCompressName, "RMF_DEM"))
2770 : {
2771 2 : return RMF_COMPRESSION_DEM;
2772 : }
2773 :
2774 0 : CPLError(CE_Failure, CPLE_AppDefined,
2775 : "RMF: Unknown compression scheme <%s>.\n"
2776 : "Defaults to NONE compression.",
2777 : pszCompressName);
2778 0 : return RMF_COMPRESSION_NONE;
2779 : }
2780 :
2781 : /************************************************************************/
2782 : /* SetupCompression() */
2783 : /************************************************************************/
2784 :
2785 243 : int RMFDataset::SetupCompression(GDALDataType eType, const char *pszFilename)
2786 : {
2787 : /* -------------------------------------------------------------------- */
2788 : /* XXX: The DEM compression method seems to be only applicable */
2789 : /* to Int32 data. */
2790 : /* -------------------------------------------------------------------- */
2791 243 : if (sHeader.iCompression == RMF_COMPRESSION_NONE)
2792 : {
2793 173 : Decompress = nullptr;
2794 173 : Compress = nullptr;
2795 : }
2796 70 : else if (sHeader.iCompression == RMF_COMPRESSION_LZW)
2797 : {
2798 57 : Decompress = &LZWDecompress;
2799 57 : Compress = &LZWCompress;
2800 57 : SetMetadataItem("COMPRESSION", "LZW", "IMAGE_STRUCTURE");
2801 : }
2802 13 : else if (sHeader.iCompression == RMF_COMPRESSION_JPEG)
2803 : {
2804 3 : if (eType != GDT_Byte || nBands != RMF_JPEG_BAND_COUNT ||
2805 3 : sHeader.nBitDepth != 24)
2806 : {
2807 0 : CPLError(CE_Failure, CPLE_AppDefined,
2808 : "RMF support only 24 bpp JPEG compressed files.");
2809 0 : return CE_Failure;
2810 : }
2811 : #ifdef HAVE_LIBJPEG
2812 6 : CPLString oBuf;
2813 3 : oBuf.Printf("%d", sHeader.iJpegQuality);
2814 3 : Decompress = &JPEGDecompress;
2815 3 : Compress = &JPEGCompress;
2816 3 : SetMetadataItem("JPEG_QUALITY", oBuf.c_str(), "IMAGE_STRUCTURE");
2817 3 : SetMetadataItem("COMPRESSION", "JPEG", "IMAGE_STRUCTURE");
2818 : #else // HAVE_LIBJPEG
2819 : CPLError(CE_Failure, CPLE_AppDefined,
2820 : "JPEG codec is needed to open <%s>.\n"
2821 : "Please rebuild GDAL with libjpeg support.",
2822 : pszFilename);
2823 : return CE_Failure;
2824 : #endif // HAVE_LIBJPEG
2825 : }
2826 10 : else if (sHeader.iCompression == RMF_COMPRESSION_DEM &&
2827 10 : eType == GDT_Int32 && nBands == RMF_DEM_BAND_COUNT)
2828 : {
2829 10 : Decompress = &DEMDecompress;
2830 10 : Compress = &DEMCompress;
2831 10 : SetMetadataItem("COMPRESSION", "RMF_DEM", "IMAGE_STRUCTURE");
2832 : }
2833 : else
2834 : {
2835 0 : CPLError(CE_Failure, CPLE_AppDefined,
2836 0 : "Unknown compression #%d at file <%s>.", sHeader.iCompression,
2837 : pszFilename);
2838 0 : return CE_Failure;
2839 : }
2840 :
2841 243 : return CE_None;
2842 : }
2843 :
2844 190 : void RMFDataset::WriteTileJobFunc(void *pData)
2845 : {
2846 190 : RMFCompressionJob *psJob = static_cast<RMFCompressionJob *>(pData);
2847 190 : RMFDataset *poDS = psJob->poDS;
2848 :
2849 : GByte *pabyTileData;
2850 : size_t nTileSize;
2851 :
2852 190 : if (poDS->Compress)
2853 : {
2854 : // RMF doesn't store compressed tiles with size greater than 80% of
2855 : // uncompressed size
2856 131 : GUInt32 nMaxCompressedTileSize =
2857 131 : static_cast<GUInt32>((psJob->nUncompressedBytes * 8) / 10);
2858 : size_t nCompressedBytes =
2859 262 : poDS->Compress(psJob->pabyUncompressedData,
2860 131 : static_cast<GUInt32>(psJob->nUncompressedBytes),
2861 : psJob->pabyCompressedData, nMaxCompressedTileSize,
2862 : psJob->nXSize, psJob->nYSize, poDS);
2863 131 : if (nCompressedBytes == 0)
2864 : {
2865 28 : pabyTileData = psJob->pabyUncompressedData;
2866 28 : nTileSize = psJob->nUncompressedBytes;
2867 : }
2868 : else
2869 : {
2870 103 : pabyTileData = psJob->pabyCompressedData;
2871 103 : nTileSize = nCompressedBytes;
2872 : }
2873 : }
2874 : else
2875 : {
2876 59 : pabyTileData = psJob->pabyUncompressedData;
2877 59 : nTileSize = psJob->nUncompressedBytes;
2878 : }
2879 :
2880 : {
2881 190 : CPLMutexHolder oHolder(poDS->poCompressData->hWriteTileMutex);
2882 190 : psJob->eResult = poDS->WriteRawTile(
2883 : psJob->nBlockXOff, psJob->nBlockYOff, pabyTileData, nTileSize);
2884 : }
2885 190 : if (poDS->poCompressData->oThreadPool.GetThreadCount() > 0)
2886 : {
2887 188 : CPLMutexHolder oHolder(poDS->poCompressData->hReadyJobMutex);
2888 94 : poDS->poCompressData->asReadyJobs.push_back(psJob);
2889 : }
2890 190 : }
2891 :
2892 55 : CPLErr RMFDataset::InitCompressorData(char **papszParamList)
2893 : {
2894 : const char *pszNumThreads =
2895 55 : CSLFetchNameValue(papszParamList, "NUM_THREADS");
2896 55 : if (pszNumThreads == nullptr)
2897 51 : pszNumThreads = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
2898 :
2899 55 : int nThreads = 0;
2900 55 : if (pszNumThreads != nullptr)
2901 : {
2902 8 : nThreads = EQUAL(pszNumThreads, "ALL_CPUS") ? CPLGetNumCPUs()
2903 4 : : atoi(pszNumThreads);
2904 : }
2905 :
2906 55 : if (nThreads < 0)
2907 : {
2908 0 : nThreads = 0;
2909 : }
2910 55 : if (nThreads > 1024)
2911 : {
2912 0 : nThreads = 1024;
2913 : }
2914 :
2915 55 : poCompressData = std::make_shared<RMFCompressData>();
2916 55 : if (nThreads > 0)
2917 : {
2918 3 : if (!poCompressData->oThreadPool.Setup(nThreads, nullptr, nullptr))
2919 : {
2920 0 : CPLError(CE_Failure, CPLE_AppDefined,
2921 : "Can't setup %d compressor threads", nThreads);
2922 0 : return CE_Failure;
2923 : }
2924 : }
2925 :
2926 55 : poCompressData->asJobs.resize(nThreads + 1);
2927 :
2928 55 : size_t nMaxTileBytes =
2929 55 : sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
2930 : size_t nCompressBufferSize =
2931 55 : 2 * nMaxTileBytes * poCompressData->asJobs.size();
2932 110 : poCompressData->pabyBuffers =
2933 55 : static_cast<GByte *>(VSIMalloc(nCompressBufferSize));
2934 :
2935 55 : CPLDebug("RMF", "Setup %d compressor threads and allocate %lu bytes buffer",
2936 : nThreads, static_cast<unsigned long>(nCompressBufferSize));
2937 55 : if (poCompressData->pabyBuffers == nullptr)
2938 : {
2939 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2940 : "Can't allocate compress buffer of size %lu.",
2941 : static_cast<unsigned long>(nCompressBufferSize));
2942 0 : return CE_Failure;
2943 : }
2944 :
2945 122 : for (size_t i = 0; i != poCompressData->asJobs.size(); ++i)
2946 : {
2947 67 : RMFCompressionJob &sJob(poCompressData->asJobs[i]);
2948 67 : sJob.pabyCompressedData =
2949 67 : poCompressData->pabyBuffers + 2 * i * nMaxTileBytes;
2950 67 : sJob.pabyUncompressedData = sJob.pabyCompressedData + nMaxTileBytes;
2951 67 : poCompressData->asReadyJobs.push_back(&sJob);
2952 : }
2953 :
2954 55 : if (nThreads > 0)
2955 : {
2956 3 : poCompressData->hReadyJobMutex = CPLCreateMutex();
2957 3 : CPLReleaseMutex(poCompressData->hReadyJobMutex);
2958 3 : poCompressData->hWriteTileMutex = CPLCreateMutex();
2959 3 : CPLReleaseMutex(poCompressData->hWriteTileMutex);
2960 : }
2961 :
2962 55 : return CE_None;
2963 : }
2964 :
2965 190 : CPLErr RMFDataset::WriteTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
2966 : size_t nBytes, GUInt32 nRawXSize,
2967 : GUInt32 nRawYSize)
2968 : {
2969 190 : RMFCompressionJob *poJob = nullptr;
2970 190 : if (poCompressData == nullptr)
2971 : {
2972 0 : CPLError(CE_Failure, CPLE_AppDefined, "RMF: Compress data is null");
2973 0 : return CE_Failure;
2974 : }
2975 :
2976 190 : if (poCompressData->oThreadPool.GetThreadCount() > 0)
2977 : {
2978 94 : size_t nJobs(poCompressData->asJobs.size());
2979 :
2980 94 : poCompressData->oThreadPool.WaitCompletion(static_cast<int>(nJobs - 1));
2981 :
2982 188 : CPLMutexHolder oHolder(poCompressData->hReadyJobMutex);
2983 94 : CPLAssert(!poCompressData->asReadyJobs.empty());
2984 94 : poJob = poCompressData->asReadyJobs.front();
2985 94 : poCompressData->asReadyJobs.pop_front();
2986 : }
2987 : else
2988 : {
2989 96 : poJob = poCompressData->asReadyJobs.front();
2990 : }
2991 :
2992 190 : if (poJob->eResult != CE_None)
2993 : {
2994 : // One of the previous jobs is not done.
2995 : // Detailed debug message is already emitted from WriteRawTile
2996 0 : return poJob->eResult;
2997 : }
2998 190 : poJob->poDS = this;
2999 190 : poJob->eResult = CE_Failure;
3000 190 : poJob->nBlockXOff = nBlockXOff;
3001 190 : poJob->nBlockYOff = nBlockYOff;
3002 190 : poJob->nUncompressedBytes = nBytes;
3003 190 : poJob->nXSize = nRawXSize;
3004 190 : poJob->nYSize = nRawYSize;
3005 :
3006 190 : memcpy(poJob->pabyUncompressedData, pabyData, nBytes);
3007 :
3008 190 : if (poCompressData->oThreadPool.GetThreadCount() > 0)
3009 : {
3010 94 : if (!poCompressData->oThreadPool.SubmitJob(WriteTileJobFunc, poJob))
3011 : {
3012 0 : CPLError(CE_Failure, CPLE_NotSupported,
3013 : "Can't submit job to thread pool.");
3014 0 : return CE_Failure;
3015 : }
3016 : }
3017 : else
3018 : {
3019 96 : WriteTileJobFunc(poJob);
3020 96 : if (poJob->eResult != CE_None)
3021 : {
3022 0 : return poJob->eResult;
3023 : }
3024 : }
3025 :
3026 190 : return CE_None;
3027 : }
3028 :
3029 190 : CPLErr RMFDataset::WriteRawTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
3030 : size_t nTileBytes)
3031 : {
3032 190 : CPLAssert(nBlockXOff >= 0 && nBlockYOff >= 0 && pabyData != nullptr &&
3033 : nTileBytes > 0);
3034 :
3035 190 : const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
3036 :
3037 190 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
3038 190 : size_t nTileSize = static_cast<size_t>(paiTiles[2 * nTile + 1]);
3039 :
3040 190 : if (nTileOffset && nTileSize <= nTileBytes)
3041 : {
3042 0 : if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
3043 : {
3044 0 : CPLError(
3045 : CE_Failure, CPLE_FileIO,
3046 : "Can't seek to offset %ld in output file to write data.\n%s",
3047 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3048 0 : return CE_Failure;
3049 : }
3050 : }
3051 : else
3052 : {
3053 190 : if (VSIFSeekL(fp, 0, SEEK_END) < 0)
3054 : {
3055 0 : CPLError(
3056 : CE_Failure, CPLE_FileIO,
3057 : "Can't seek to offset %ld in output file to write data.\n%s",
3058 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3059 0 : return CE_Failure;
3060 : }
3061 190 : nTileOffset = VSIFTellL(fp);
3062 190 : vsi_l_offset nNewTileOffset = 0;
3063 190 : paiTiles[2 * nTile] = GetRMFOffset(nTileOffset, &nNewTileOffset);
3064 :
3065 190 : if (nTileOffset != nNewTileOffset)
3066 : {
3067 23 : if (VSIFSeekL(fp, nNewTileOffset, SEEK_SET) < 0)
3068 : {
3069 0 : CPLError(CE_Failure, CPLE_FileIO,
3070 : "Can't seek to offset %ld in output file to "
3071 : "write data.\n%s",
3072 0 : static_cast<long>(nNewTileOffset), VSIStrerror(errno));
3073 0 : return CE_Failure;
3074 : }
3075 : }
3076 190 : bHeaderDirty = true;
3077 : }
3078 :
3079 : #ifdef CPL_MSB
3080 : // Compressed tiles are already with proper byte order
3081 : if (eRMFType == RMFT_MTW && sHeader.iCompression == RMF_COMPRESSION_NONE)
3082 : {
3083 : // Byte swap can be done in place
3084 : if (sHeader.nBitDepth == 16)
3085 : {
3086 : for (size_t i = 0; i < nTileBytes; i += 2)
3087 : CPL_SWAP16PTR(pabyData + i);
3088 : }
3089 : else if (sHeader.nBitDepth == 32)
3090 : {
3091 : for (size_t i = 0; i < nTileBytes; i += 4)
3092 : CPL_SWAP32PTR(pabyData + i);
3093 : }
3094 : else if (sHeader.nBitDepth == 64)
3095 : {
3096 : for (size_t i = 0; i < nTileBytes; i += 8)
3097 : CPL_SWAPDOUBLE(pabyData + i);
3098 : }
3099 : }
3100 : #endif
3101 :
3102 190 : bool bOk = (VSIFWriteL(pabyData, 1, nTileBytes, fp) == nTileBytes);
3103 :
3104 190 : if (!bOk)
3105 : {
3106 0 : CPLError(CE_Failure, CPLE_FileIO,
3107 : "Can't write tile with X offset %d and Y offset %d.\n%s",
3108 0 : nBlockXOff, nBlockYOff, VSIStrerror(errno));
3109 0 : return CE_Failure;
3110 : }
3111 :
3112 190 : paiTiles[2 * nTile + 1] = static_cast<GUInt32>(nTileBytes);
3113 190 : bHeaderDirty = true;
3114 :
3115 190 : return CE_None;
3116 : }
3117 :
3118 352 : CPLErr RMFDataset::ReadTile(int nBlockXOff, int nBlockYOff, GByte *pabyData,
3119 : size_t nRawBytes, GUInt32 nRawXSize,
3120 : GUInt32 nRawYSize, bool &bNullTile)
3121 : {
3122 352 : bNullTile = false;
3123 :
3124 352 : const GUInt32 nTile = nBlockYOff * nXTiles + nBlockXOff;
3125 352 : if (2 * nTile + 1 >= sHeader.nTileTblSize / sizeof(GUInt32))
3126 : {
3127 0 : return CE_Failure;
3128 : }
3129 352 : vsi_l_offset nTileOffset = GetFileOffset(paiTiles[2 * nTile]);
3130 352 : GUInt32 nTileBytes = paiTiles[2 * nTile + 1];
3131 : // RMF doesn't store compressed tiles with size greater than 80% of
3132 : // uncompressed size. But just in case, select twice as many.
3133 352 : GUInt32 nMaxTileBytes =
3134 352 : 2 * sHeader.nTileWidth * sHeader.nTileHeight * sHeader.nBitDepth / 8;
3135 :
3136 352 : if (nTileBytes >= nMaxTileBytes)
3137 : {
3138 0 : CPLError(CE_Failure, CPLE_AppDefined,
3139 : "Invalid tile size %lu at offset %ld. Must be less than %lu",
3140 : static_cast<unsigned long>(nTileBytes),
3141 : static_cast<long>(nTileOffset),
3142 : static_cast<unsigned long>(nMaxTileBytes));
3143 0 : return CE_Failure;
3144 : }
3145 :
3146 352 : if (nTileOffset == 0)
3147 : {
3148 1 : bNullTile = true;
3149 1 : return CE_None;
3150 : }
3151 :
3152 : #ifdef DEBUG
3153 351 : CPLDebug("RMF", "Read RawSize [%d, %d], nTileBytes %d, nRawBytes %d",
3154 : nRawXSize, nRawYSize, static_cast<int>(nTileBytes),
3155 : static_cast<int>(nRawBytes));
3156 : #endif // DEBUG
3157 :
3158 351 : if (VSIFSeekL(fp, nTileOffset, SEEK_SET) < 0)
3159 : {
3160 : // XXX: We will not report error here, because file just may be
3161 : // in update state and data for this block will be available later
3162 0 : if (eAccess == GA_Update)
3163 0 : return CE_None;
3164 :
3165 0 : CPLError(CE_Failure, CPLE_FileIO,
3166 : "Can't seek to offset %ld in input file to read data.\n%s",
3167 0 : static_cast<long>(nTileOffset), VSIStrerror(errno));
3168 0 : return CE_Failure;
3169 : }
3170 :
3171 351 : if (Decompress == nullptr || nTileBytes == nRawBytes)
3172 : {
3173 166 : if (nTileBytes != nRawBytes)
3174 : {
3175 0 : CPLError(CE_Failure, CPLE_AppDefined,
3176 : "RMF: Invalid tile size %lu, expected %lu",
3177 : static_cast<unsigned long>(nTileBytes),
3178 : static_cast<unsigned long>(nRawBytes));
3179 0 : return CE_Failure;
3180 : }
3181 :
3182 166 : if (VSIFReadL(pabyData, 1, nRawBytes, fp) < nRawBytes)
3183 : {
3184 0 : CPLError(CE_Failure, CPLE_FileIO,
3185 : "RMF: Can't read at offset %lu from input file.\n%s",
3186 : static_cast<unsigned long>(nTileOffset),
3187 0 : VSIStrerror(errno));
3188 0 : return CE_Failure;
3189 : }
3190 :
3191 : #ifdef CPL_MSB
3192 : if (eRMFType == RMFT_MTW)
3193 : {
3194 : if (sHeader.nBitDepth == 16)
3195 : {
3196 : for (GUInt32 i = 0; i < nRawBytes; i += 2)
3197 : CPL_SWAP16PTR(pabyData + i);
3198 : }
3199 : else if (sHeader.nBitDepth == 32)
3200 : {
3201 : for (GUInt32 i = 0; i < nRawBytes; i += 4)
3202 : CPL_SWAP32PTR(pabyData + i);
3203 : }
3204 : else if (sHeader.nBitDepth == 64)
3205 : {
3206 : for (GUInt32 i = 0; i < nRawBytes; i += 8)
3207 : CPL_SWAPDOUBLE(pabyData + i);
3208 : }
3209 : }
3210 : #endif
3211 166 : return CE_None;
3212 : }
3213 :
3214 185 : if (pabyDecompressBuffer == nullptr)
3215 : {
3216 21 : pabyDecompressBuffer =
3217 21 : static_cast<GByte *>(VSIMalloc(std::max(1U, nMaxTileBytes)));
3218 21 : if (!pabyDecompressBuffer)
3219 : {
3220 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
3221 : "Can't allocate decompress buffer of size %lu.\n%s",
3222 : static_cast<unsigned long>(nMaxTileBytes),
3223 0 : VSIStrerror(errno));
3224 0 : return CE_Failure;
3225 : }
3226 : }
3227 :
3228 185 : if (VSIFReadL(pabyDecompressBuffer, 1, nTileBytes, fp) < nTileBytes)
3229 : {
3230 0 : CPLError(CE_Failure, CPLE_FileIO,
3231 : "RMF: Can't read at offset %lu from input file.\n%s",
3232 0 : static_cast<unsigned long>(nTileOffset), VSIStrerror(errno));
3233 0 : return CE_Failure;
3234 : }
3235 :
3236 : size_t nDecompressedSize =
3237 185 : Decompress(pabyDecompressBuffer, nTileBytes, pabyData,
3238 : static_cast<GUInt32>(nRawBytes), nRawXSize, nRawYSize);
3239 :
3240 185 : if (nDecompressedSize != static_cast<size_t>(nRawBytes))
3241 : {
3242 0 : CPLError(CE_Failure, CPLE_FileIO,
3243 : "Can't decompress tile xOff %d yOff %d. "
3244 : "Raw tile size is %lu but decompressed is %lu. "
3245 : "Compressed tile size is %lu",
3246 : nBlockXOff, nBlockYOff, static_cast<unsigned long>(nRawBytes),
3247 : static_cast<unsigned long>(nDecompressedSize),
3248 : static_cast<unsigned long>(nTileBytes));
3249 0 : return CE_Failure;
3250 : }
3251 : // We don't need to swap bytes here,
3252 : // because decompressed data is in proper byte order
3253 185 : return CE_None;
3254 : }
3255 :
3256 277 : void RMFDataset::SetupNBits()
3257 : {
3258 277 : int nBitDepth = 0;
3259 277 : if (sHeader.nBitDepth < 8 && nBands == 1)
3260 : {
3261 6 : nBitDepth = static_cast<int>(sHeader.nBitDepth);
3262 : }
3263 271 : else if (sHeader.nBitDepth == 16 && nBands == 3 && eRMFType == RMFT_RSW)
3264 : {
3265 0 : nBitDepth = 5;
3266 : }
3267 :
3268 277 : if (nBitDepth > 0)
3269 : {
3270 6 : char szNBits[32] = {};
3271 6 : snprintf(szNBits, sizeof(szNBits), "%d", nBitDepth);
3272 12 : for (int iBand = 1; iBand <= nBands; iBand++)
3273 : {
3274 6 : GetRasterBand(iBand)->SetMetadataItem("NBITS", szNBits,
3275 6 : "IMAGE_STRUCTURE");
3276 : }
3277 : }
3278 277 : }
3279 :
3280 : /************************************************************************/
3281 : /* GDALRegister_RMF() */
3282 : /************************************************************************/
3283 :
3284 1964 : void GDALRegister_RMF()
3285 :
3286 : {
3287 1964 : if (GDALGetDriverByName("RMF") != nullptr)
3288 283 : return;
3289 :
3290 1681 : GDALDriver *poDriver = new GDALDriver();
3291 :
3292 1681 : poDriver->SetDescription("RMF");
3293 1681 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3294 1681 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Raster Matrix Format");
3295 1681 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/rmf.html");
3296 1681 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rsw");
3297 1681 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
3298 1681 : "Byte Int16 Int32 Float64");
3299 1681 : poDriver->SetMetadataItem(
3300 : GDAL_DMD_CREATIONOPTIONLIST,
3301 : "<CreationOptionList>"
3302 : " <Option name='MTW' type='boolean' description='Create MTW DEM "
3303 : "matrix'/>"
3304 : " <Option name='BLOCKXSIZE' type='int' description='Tile Width'/>"
3305 : " <Option name='BLOCKYSIZE' type='int' description='Tile Height'/>"
3306 : " <Option name='RMFHUGE' type='string-select' description='Creation "
3307 : "of huge RMF file (Supported by GIS Panorama since v11)'>"
3308 : " <Value>NO</Value>"
3309 : " <Value>YES</Value>"
3310 : " <Value>IF_SAFER</Value>"
3311 : " </Option>"
3312 : " <Option name='COMPRESS' type='string-select' default='NONE'>"
3313 : " <Value>NONE</Value>"
3314 : " <Value>LZW</Value>"
3315 : " <Value>JPEG</Value>"
3316 : " <Value>RMF_DEM</Value>"
3317 : " </Option>"
3318 : " <Option name='JPEG_QUALITY' type='int' description='JPEG quality "
3319 : "1-100' default='75'/>"
3320 : " <Option name='NUM_THREADS' type='string' description='Number of "
3321 : "worker threads for compression. Can be set to ALL_CPUS' default='1'/>"
3322 1681 : "</CreationOptionList>");
3323 1681 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3324 :
3325 1681 : poDriver->pfnIdentify = RMFDataset::Identify;
3326 1681 : poDriver->pfnOpen = RMFDataset::Open;
3327 1681 : poDriver->pfnCreate = RMFDataset::Create;
3328 1681 : poDriver->SetMetadataItem(
3329 : GDAL_DMD_OPENOPTIONLIST,
3330 : "<OpenOptionList>"
3331 : " <Option name='RMF_SET_VERTCS' type='string' description='Layers "
3332 : "spatial reference will include vertical coordinate system description "
3333 : "if exist' default='NO'/>"
3334 1681 : "</OpenOptionList>");
3335 :
3336 1681 : GetGDALDriverManager()->RegisterDriver(poDriver);
3337 : }
3338 :
3339 : /************************************************************************/
3340 : /* RMFCompressData */
3341 : /************************************************************************/
3342 :
3343 55 : RMFCompressData::RMFCompressData() : pabyBuffers(nullptr)
3344 : {
3345 55 : }
3346 :
3347 55 : RMFCompressData::~RMFCompressData()
3348 : {
3349 55 : if (pabyBuffers != nullptr)
3350 : {
3351 55 : VSIFree(pabyBuffers);
3352 : }
3353 :
3354 55 : if (hWriteTileMutex != nullptr)
3355 : {
3356 3 : CPLDestroyMutex(hWriteTileMutex);
3357 : }
3358 :
3359 55 : if (hReadyJobMutex != nullptr)
3360 : {
3361 3 : CPLDestroyMutex(hReadyJobMutex);
3362 : }
3363 55 : }
3364 :
3365 : GDALSuggestedBlockAccessPattern
3366 21 : RMFRasterBand::GetSuggestedBlockAccessPattern() const
3367 : {
3368 21 : return GSBAP_RANDOM;
3369 : }
3370 :
3371 1507 : CPLErr RMFDataset::SetMetadataItem(const char *pszName, const char *pszValue,
3372 : const char *pszDomain)
3373 : {
3374 1507 : if (GetAccess() == GA_Update)
3375 : {
3376 190 : CPLDebug("RMF", "SetMetadataItem: %s=%s", pszName, pszValue);
3377 190 : if (EQUAL(pszName, MD_NAME_KEY))
3378 : {
3379 20 : memcpy(sHeader.byName, pszValue,
3380 : CPLStrnlen(pszValue, RMF_NAME_SIZE));
3381 20 : bHeaderDirty = true;
3382 : }
3383 170 : else if (EQUAL(pszName, MD_SCALE_KEY) && CPLStrnlen(pszValue, 10) > 4)
3384 : {
3385 20 : sHeader.dfScale = atof(pszValue + 4);
3386 20 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
3387 20 : bHeaderDirty = true;
3388 : }
3389 150 : else if (EQUAL(pszName, MD_FRAME_KEY))
3390 : {
3391 0 : bHeaderDirty = true;
3392 : }
3393 : }
3394 1507 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
3395 : }
3396 :
3397 39 : CPLErr RMFDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
3398 : {
3399 39 : if (GetAccess() == GA_Update)
3400 : {
3401 39 : auto pszName = CSLFetchNameValue(papszMetadata, MD_NAME_KEY);
3402 39 : if (pszName != nullptr)
3403 : {
3404 21 : memcpy(sHeader.byName, pszName, CPLStrnlen(pszName, RMF_NAME_SIZE));
3405 21 : bHeaderDirty = true;
3406 :
3407 21 : CPLDebug("RMF", "SetMetadata: %s", pszName);
3408 : }
3409 39 : auto pszScale = CSLFetchNameValue(papszMetadata, MD_SCALE_KEY);
3410 39 : if (pszScale != nullptr && CPLStrnlen(pszScale, 10) > 4)
3411 : {
3412 21 : sHeader.dfScale = atof(pszScale + 4);
3413 21 : sHeader.dfResolution = sHeader.dfScale / sHeader.dfPixelSize;
3414 21 : bHeaderDirty = true;
3415 :
3416 21 : CPLDebug("RMF", "SetMetadata: %s", pszScale);
3417 : }
3418 39 : auto pszFrame = CSLFetchNameValue(papszMetadata, MD_FRAME_KEY);
3419 39 : if (pszFrame != nullptr)
3420 : {
3421 2 : bHeaderDirty = true;
3422 :
3423 2 : CPLDebug("RMF", "SetMetadata: %s", pszFrame);
3424 : }
3425 : }
3426 39 : return GDALDataset::SetMetadata(papszMetadata, pszDomain);
3427 : }
|