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