Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: JPEG2000 driver based on OpenJPEG or Grok library
4 : * Purpose: JPEG2000 driver based on OpenJPEG or Grok library
5 : * Authors: Even Rouault, <even dot rouault at spatialys dot com>
6 : * Aaron Boxer, <boxerab at protonmail dot com>
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys dot com>
10 : * Copyright (c) 2015, European Union (European Environment Agency)
11 : * Copyright (c) 2023, Grok Image Compression Inc.
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : #include <cassert>
17 : #include <vector>
18 :
19 : #include "cpl_atomic_ops.h"
20 : #include "cpl_multiproc.h"
21 : #include "cpl_string.h"
22 : #include "cpl_vsi_virtual.h"
23 : #include "cpl_worker_thread_pool.h"
24 : #include "gdal_frmts.h"
25 : #include "gdaljp2abstractdataset.h"
26 : #include "gdaljp2metadata.h"
27 : #include "vrt/vrtdataset.h"
28 :
29 : #include <algorithm>
30 :
31 : #include "jp2opjlikedataset.h"
32 :
33 : /************************************************************************/
34 : /* JP2OPJLikeRasterBand() */
35 : /************************************************************************/
36 :
37 : template <typename CODEC, typename BASE>
38 1150 : JP2OPJLikeRasterBand<CODEC, BASE>::JP2OPJLikeRasterBand(
39 : JP2OPJLikeDataset<CODEC, BASE> *poDSIn, int nBandIn,
40 : GDALDataType eDataTypeIn, int nBits, int bPromoteTo8BitIn,
41 1150 : int nBlockXSizeIn, int nBlockYSizeIn)
42 :
43 : {
44 1150 : this->eDataType = eDataTypeIn;
45 1150 : this->nBlockXSize = nBlockXSizeIn;
46 1150 : this->nBlockYSize = nBlockYSizeIn;
47 1150 : this->bPromoteTo8Bit = bPromoteTo8BitIn;
48 1150 : poCT = nullptr;
49 :
50 1150 : if ((nBits % 8) != 0)
51 41 : GDALRasterBand::SetMetadataItem(
52 82 : "NBITS", CPLString().Printf("%d", nBits), "IMAGE_STRUCTURE");
53 1150 : GDALRasterBand::SetMetadataItem("COMPRESSION", "JPEG2000",
54 : "IMAGE_STRUCTURE");
55 1150 : this->poDS = poDSIn;
56 1150 : this->nBand = nBandIn;
57 1150 : }
58 :
59 : template <typename CODEC, typename BASE>
60 50 : GDALColorTable *JP2OPJLikeRasterBand<CODEC, BASE>::GetColorTable()
61 : {
62 50 : return poCT;
63 : }
64 :
65 : template <typename CODEC, typename BASE>
66 0 : int JP2OPJLikeRasterBand<CODEC, BASE>::HasArbitraryOverviews()
67 : {
68 0 : return poCT == nullptr;
69 : }
70 :
71 : /************************************************************************/
72 : /* MayMultiBlockReadingBeMultiThreaded() */
73 : /************************************************************************/
74 :
75 : template <typename CODEC, typename BASE>
76 0 : bool JP2OPJLikeRasterBand<CODEC, BASE>::MayMultiBlockReadingBeMultiThreaded()
77 : const
78 : {
79 0 : auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
80 0 : return poGDS->GetNumThreads() > 1;
81 : }
82 :
83 : /************************************************************************/
84 : /* ~JP2OPJLikeRasterBand() */
85 : /************************************************************************/
86 :
87 : template <typename CODEC, typename BASE>
88 2300 : JP2OPJLikeRasterBand<CODEC, BASE>::~JP2OPJLikeRasterBand()
89 : {
90 24 : delete poCT;
91 2324 : }
92 :
93 : /************************************************************************/
94 : /* CLAMP_0_255() */
95 : /************************************************************************/
96 :
97 134000 : static CPL_INLINE GByte CLAMP_0_255(int val)
98 : {
99 134000 : return static_cast<GByte>(std::clamp(val, 0, 255));
100 : }
101 :
102 : /************************************************************************/
103 : /* IReadBlock() */
104 : /************************************************************************/
105 :
106 : template <typename CODEC, typename BASE>
107 254 : CPLErr JP2OPJLikeRasterBand<CODEC, BASE>::IReadBlock(int nBlockXOff,
108 : int nBlockYOff,
109 : void *pImage)
110 : {
111 254 : auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
112 :
113 : #ifdef DEBUG_VERBOSE
114 : int nXOff = nBlockXOff * nBlockXSize;
115 : int nYOff = nBlockYOff * nBlockYSize;
116 : int nXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
117 : int nYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
118 : if (poGDS->iLevel == 0)
119 : {
120 : CPLDebug(CODEC::debugId(),
121 : "ds.GetRasterBand(%d).ReadRaster(%d,%d,%d,%d)", nBand, nXOff,
122 : nYOff, nXSize, nYSize);
123 : }
124 : else
125 : {
126 : CPLDebug(CODEC::debugId(),
127 : "ds.GetRasterBand(%d).GetOverview(%d).ReadRaster(%d,%d,%d,%d)",
128 : nBand, poGDS->iLevel - 1, nXOff, nYOff, nXSize, nYSize);
129 : }
130 : #endif
131 :
132 254 : if (poGDS->bEnoughMemoryToLoadOtherBands)
133 : return poGDS->ReadBlock(nBand, poGDS->fp_, nBlockXOff, nBlockYOff,
134 254 : pImage, poGDS->nBands, nullptr);
135 : else
136 : return poGDS->ReadBlock(nBand, poGDS->fp_, nBlockXOff, nBlockYOff,
137 0 : pImage, 1, &nBand);
138 : }
139 :
140 : /************************************************************************/
141 : /* IRasterIO() */
142 : /************************************************************************/
143 :
144 : template <typename CODEC, typename BASE>
145 227 : CPLErr JP2OPJLikeRasterBand<CODEC, BASE>::IRasterIO(
146 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
147 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
148 : GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
149 : {
150 227 : auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
151 :
152 227 : if (eRWFlag != GF_Read)
153 0 : return CE_Failure;
154 :
155 : /* ==================================================================== */
156 : /* Do we have overviews that would be appropriate to satisfy */
157 : /* this request? */
158 : /* ==================================================================== */
159 227 : if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
160 : {
161 : int bTried;
162 1 : CPLErr eErr = TryOverviewRasterIO(
163 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
164 : eBufType, nPixelSpace, nLineSpace, psExtraArg, &bTried);
165 1 : if (bTried)
166 1 : return eErr;
167 : }
168 :
169 : // Check whether to skip out to block based methods.
170 226 : if (!poGDS->canPerformDirectIO())
171 : {
172 226 : int nRet = poGDS->PreloadBlocks(this, nXOff, nYOff, nXSize, nYSize, 0,
173 : nullptr);
174 226 : if (nRet < 0)
175 0 : return CE_Failure;
176 226 : poGDS->bEnoughMemoryToLoadOtherBands = nRet;
177 :
178 226 : CPLErr eErr = GDALPamRasterBand::IRasterIO(
179 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
180 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
181 :
182 : // cppcheck-suppress redundantAssignment
183 226 : poGDS->bEnoughMemoryToLoadOtherBands = TRUE;
184 226 : return eErr;
185 : }
186 :
187 : return poGDS->DirectRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
188 0 : nBufXSize, nBufYSize, eBufType, 1, &nBand,
189 0 : nPixelSpace, nLineSpace, 0, psExtraArg);
190 : }
191 :
192 : template <typename CODEC, typename BASE> struct JP2JobStruct
193 : {
194 : public:
195 : JP2OPJLikeDataset<CODEC, BASE> *poGDS_ = nullptr;
196 : int nBand = 0;
197 : std::vector<std::pair<int, int>> oPairs{};
198 : volatile int nCurPair = 0;
199 : int nBandCount = 0;
200 : const int *panBandMap = nullptr;
201 : volatile bool bSuccess = false;
202 : };
203 :
204 : /************************************************************************/
205 : /* GetFileHandle() */
206 : /************************************************************************/
207 :
208 : template <typename CODEC, typename BASE>
209 8 : VSILFILE *JP2OPJLikeDataset<CODEC, BASE>::GetFileHandle()
210 : {
211 8 : return this->fp_;
212 : }
213 :
214 : /************************************************************************/
215 : /* ReadBlockInThread() */
216 : /************************************************************************/
217 :
218 : template <typename CODEC, typename BASE>
219 17 : void JP2OPJLikeDataset<CODEC, BASE>::ReadBlockInThread(void *userdata)
220 : {
221 : int nPair;
222 17 : auto poJob = static_cast<JP2JobStruct<CODEC, BASE> *>(userdata);
223 :
224 17 : JP2OPJLikeDataset *poGDS = poJob->poGDS_;
225 17 : const int nBand = poJob->nBand;
226 17 : const int nPairs = static_cast<int>(poJob->oPairs.size());
227 17 : const int nBandCount = poJob->nBandCount;
228 17 : const int *panBandMap = poJob->panBandMap;
229 17 : VSILFILE *fp = VSIFOpenL(poGDS->m_osFilename.c_str(), "rb");
230 17 : if (fp == nullptr)
231 : {
232 0 : CPLDebug(CODEC::debugId(), "Cannot open %s",
233 : poGDS->m_osFilename.c_str());
234 0 : poJob->bSuccess = false;
235 : // VSIFree(pDummy);
236 0 : return;
237 : }
238 :
239 291 : while ((nPair = CPLAtomicInc(&(poJob->nCurPair))) < nPairs &&
240 137 : poJob->bSuccess)
241 : {
242 137 : int nBlockXOff = poJob->oPairs[nPair].first;
243 137 : int nBlockYOff = poJob->oPairs[nPair].second;
244 137 : poGDS->AcquireMutex();
245 : GDALRasterBlock *poBlock =
246 137 : poGDS->GetRasterBand(nBand)->GetLockedBlockRef(nBlockXOff,
247 : nBlockYOff, TRUE);
248 137 : poGDS->ReleaseMutex();
249 137 : if (poBlock == nullptr)
250 : {
251 0 : poJob->bSuccess = false;
252 0 : break;
253 : }
254 :
255 137 : void *pDstBuffer = poBlock->GetDataRef();
256 137 : if (poGDS->ReadBlock(nBand, fp, nBlockXOff, nBlockYOff, pDstBuffer,
257 137 : nBandCount, panBandMap) != CE_None)
258 : {
259 0 : poJob->bSuccess = false;
260 : }
261 :
262 137 : poBlock->DropLock();
263 : }
264 :
265 17 : VSIFCloseL(fp);
266 : // VSIFree(pDummy);
267 : }
268 :
269 : /************************************************************************/
270 : /* PreloadBlocks() */
271 : /************************************************************************/
272 :
273 : template <typename CODEC, typename BASE>
274 240 : int JP2OPJLikeDataset<CODEC, BASE>::PreloadBlocks(
275 : JP2OPJLikeRasterBand<CODEC, BASE> *poBand, int nXOff, int nYOff, int nXSize,
276 : int nYSize, int nBandCount, const int *panBandMap)
277 : {
278 240 : int bRet = TRUE;
279 240 : const int nXStart = nXOff / poBand->nBlockXSize;
280 240 : const int nXEnd = (nXOff + nXSize - 1) / poBand->nBlockXSize;
281 240 : const int nYStart = nYOff / poBand->nBlockYSize;
282 240 : const int nYEnd = (nYOff + nYSize - 1) / poBand->nBlockYSize;
283 480 : const GIntBig nReqMem = static_cast<GIntBig>(nXEnd - nXStart + 1) *
284 240 : (nYEnd - nYStart + 1) * poBand->nBlockXSize *
285 240 : poBand->nBlockYSize *
286 240 : GDALGetDataTypeSizeBytes(poBand->eDataType);
287 :
288 240 : const int nMaxThreads = this->GetNumThreads();
289 240 : if (!this->bUseSetDecodeArea && nMaxThreads > 1)
290 : {
291 96 : if (nReqMem > GDALGetCacheMax64() / (nBandCount == 0 ? 1 : nBandCount))
292 0 : return FALSE;
293 :
294 96 : JP2JobStruct<CODEC, BASE> oJob;
295 96 : this->m_nBlocksToLoad = 0;
296 : try
297 : {
298 226 : for (int nBlockXOff = nXStart; nBlockXOff <= nXEnd; ++nBlockXOff)
299 : {
300 484 : for (int nBlockYOff = nYStart; nBlockYOff <= nYEnd;
301 : ++nBlockYOff)
302 : {
303 354 : GDALRasterBlock *poBlock =
304 : poBand->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
305 354 : if (poBlock != nullptr)
306 : {
307 156 : poBlock->DropLock();
308 156 : continue;
309 : }
310 198 : oJob.oPairs.push_back(
311 : std::pair<int, int>(nBlockXOff, nBlockYOff));
312 198 : this->m_nBlocksToLoad++;
313 : }
314 : }
315 : }
316 0 : catch (const std::bad_alloc &)
317 : {
318 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory error");
319 0 : this->m_nBlocksToLoad = 0;
320 0 : return -1;
321 : }
322 :
323 96 : if (this->m_nBlocksToLoad > 1)
324 : {
325 5 : const int l_nThreads = std::min(this->m_nBlocksToLoad, nMaxThreads);
326 : CPLJoinableThread **pahThreads = static_cast<CPLJoinableThread **>(
327 5 : VSI_CALLOC_VERBOSE(sizeof(CPLJoinableThread *), l_nThreads));
328 5 : if (pahThreads == nullptr)
329 : {
330 0 : this->m_nBlocksToLoad = 0;
331 0 : return -1;
332 : }
333 : int i;
334 :
335 5 : CPLDebug(CODEC::debugId(), "%d blocks to load (%d threads)",
336 : this->m_nBlocksToLoad, l_nThreads);
337 :
338 5 : oJob.poGDS_ = this;
339 5 : oJob.nBand = poBand->GetBand();
340 5 : oJob.nCurPair = -1;
341 5 : if (nBandCount > 0)
342 : {
343 2 : oJob.nBandCount = nBandCount;
344 2 : oJob.panBandMap = panBandMap;
345 : }
346 : else
347 : {
348 3 : if (nReqMem <= GDALGetCacheMax64() / nBands)
349 : {
350 3 : oJob.nBandCount = nBands;
351 3 : oJob.panBandMap = nullptr;
352 : }
353 : else
354 : {
355 0 : bRet = FALSE;
356 0 : oJob.nBandCount = 1;
357 0 : oJob.panBandMap = &oJob.nBand;
358 : }
359 : }
360 5 : oJob.bSuccess = true;
361 :
362 : /* Flushes all dirty blocks from cache to disk to avoid them */
363 : /* to be flushed randomly, and simultaneously, from our worker
364 : * threads, */
365 : /* which might cause races in the output driver. */
366 : /* This is a workaround to a design defect of the block cache */
367 5 : GDALRasterBlock::FlushDirtyBlocks();
368 :
369 22 : for (i = 0; i < l_nThreads; i++)
370 : {
371 34 : pahThreads[i] =
372 17 : CPLCreateJoinableThread(ReadBlockInThread, &oJob);
373 17 : if (pahThreads[i] == nullptr)
374 0 : oJob.bSuccess = false;
375 : }
376 5 : TemporarilyDropReadWriteLock();
377 22 : for (i = 0; i < l_nThreads; i++)
378 17 : CPLJoinThread(pahThreads[i]);
379 5 : ReacquireReadWriteLock();
380 5 : CPLFree(pahThreads);
381 5 : if (!oJob.bSuccess)
382 : {
383 0 : this->m_nBlocksToLoad = 0;
384 0 : return -1;
385 : }
386 5 : this->m_nBlocksToLoad = 0;
387 : }
388 : }
389 :
390 240 : return bRet;
391 : }
392 :
393 : /************************************************************************/
394 : /* GetEstimatedRAMUsage() */
395 : /************************************************************************/
396 :
397 : template <typename CODEC, typename BASE>
398 100 : GIntBig JP2OPJLikeDataset<CODEC, BASE>::GetEstimatedRAMUsage()
399 : {
400 : // libopenjp2 holds the code block values in a uint32_t array.
401 100 : GIntBig nVal = static_cast<GIntBig>(this->m_nTileWidth) *
402 100 : this->m_nTileHeight * this->nBands * sizeof(uint32_t);
403 100 : if (this->bSingleTiled)
404 : {
405 : // libopenjp2 ingests the codestream for a whole tile. So for a
406 : // single-tiled image, this is roughly the size of the file.
407 100 : const auto nCurPos = VSIFTellL(this->fp_);
408 100 : VSIFSeekL(this->fp_, 0, SEEK_END);
409 100 : nVal += VSIFTellL(this->fp_);
410 100 : VSIFSeekL(this->fp_, nCurPos, SEEK_SET);
411 : }
412 100 : CPLDebug(CODEC::debugId(), "Estimated RAM usage for %s: %.2f GB",
413 100 : GetDescription(), static_cast<double>(nVal * 1e-9));
414 100 : return nVal;
415 : }
416 :
417 : template <typename CODEC, typename BASE>
418 18 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::AdviseRead(
419 : int nXOff, int nYOff, int nXSize, int nYSize, int nBufXSize, int nBufYSize,
420 : GDALDataType eDT, int nBandCount, int *panBandList,
421 : CSLConstList papszOptions)
422 : {
423 :
424 18 : return BASE::AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
425 18 : eDT, nBandCount, panBandList, papszOptions);
426 : }
427 :
428 : /************************************************************************/
429 : /* IRasterIO() */
430 : /************************************************************************/
431 :
432 : template <typename CODEC, typename BASE>
433 15 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::IRasterIO(
434 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
435 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
436 : int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
437 : GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
438 : {
439 15 : if (eRWFlag != GF_Read)
440 0 : return CE_Failure;
441 :
442 15 : if (nBandCount < 1)
443 0 : return CE_Failure;
444 :
445 15 : auto poBand = cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
446 : GetRasterBand(panBandMap[0]));
447 :
448 : /* ==================================================================== */
449 : /* Do we have overviews that would be appropriate to satisfy */
450 : /* this request? */
451 : /* ==================================================================== */
452 :
453 16 : if ((nBufXSize < nXSize || nBufYSize < nYSize) &&
454 1 : poBand->GetOverviewCount() > 0)
455 : {
456 : int bTried;
457 1 : CPLErr eErr = TryOverviewRasterIO(
458 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
459 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
460 : nBandSpace, psExtraArg, &bTried);
461 1 : if (bTried)
462 1 : return eErr;
463 : }
464 :
465 14 : CPLErr eErr = CE_None;
466 14 : [[maybe_unused]] int nBand = 0; /* 1 based */
467 14 : if (!BASE::canPerformDirectIO())
468 : {
469 14 : int nRet = PreloadBlocks(poBand, nXOff, nYOff, nXSize, nYSize,
470 : nBandCount, panBandMap);
471 14 : if (nRet < 0)
472 0 : return CE_Failure;
473 :
474 14 : this->bEnoughMemoryToLoadOtherBands = nRet;
475 :
476 14 : eErr = GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
477 : pData, nBufXSize, nBufYSize, eBufType,
478 : nBandCount, panBandMap, nPixelSpace,
479 : nLineSpace, nBandSpace, psExtraArg);
480 :
481 14 : return eErr;
482 : }
483 :
484 0 : eErr = BASE::DirectRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
485 : nBufXSize, nBufYSize, eBufType, nBandCount,
486 : panBandMap, nPixelSpace, nLineSpace, nBandSpace,
487 : psExtraArg);
488 :
489 0 : this->bEnoughMemoryToLoadOtherBands = TRUE;
490 0 : return eErr;
491 : }
492 :
493 : /************************************************************************/
494 : /* IBuildOverviews() */
495 : /************************************************************************/
496 :
497 : template <typename CODEC, typename BASE>
498 3 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::IBuildOverviews(
499 : const char *pszResampling, int nOverviews, const int *panOverviewList,
500 : int nListBands, const int *panBandList, GDALProgressFunc pfnProgress,
501 : void *pProgressData, CSLConstList papszOptions)
502 :
503 : {
504 : // In order for building external overviews to work properly, we
505 : // discard any concept of internal overviews when the user
506 : // first requests to build external overviews.
507 5 : for (int i = 0; i < this->nOverviewCount; i++)
508 : {
509 2 : delete papoOverviewDS[i];
510 : }
511 3 : CPLFree(papoOverviewDS);
512 3 : papoOverviewDS = nullptr;
513 3 : this->nOverviewCount = 0;
514 :
515 3 : return GDALPamDataset::IBuildOverviews(
516 : pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
517 3 : pfnProgress, pProgressData, papszOptions);
518 : }
519 :
520 : /************************************************************************/
521 : /* ReadBlock() */
522 : /************************************************************************/
523 :
524 : template <typename CODEC, typename BASE>
525 391 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::ReadBlock(int nBand, VSILFILE *fpIn,
526 : int nBlockXOff, int nBlockYOff,
527 : void *pImage, int nBandCount,
528 : const int *panBandMap)
529 : {
530 391 : CPLErr eErr = CE_None;
531 0 : CODEC localctx;
532 :
533 391 : auto poBand = cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
534 : GetRasterBand(nBand));
535 391 : const int nBlockXSize = poBand->nBlockXSize;
536 391 : const int nBlockYSize = poBand->nBlockYSize;
537 391 : const GDALDataType eDataType = poBand->eDataType;
538 :
539 391 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
540 :
541 391 : const int nTileNumber = nBlockXOff + nBlockYOff * poBand->nBlocksPerRow;
542 391 : const int nWidthToRead =
543 391 : std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
544 391 : const int nHeightToRead =
545 391 : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
546 :
547 391 : eErr = this->readBlockInit(fpIn, &localctx, nBlockXOff, nBlockYOff,
548 : this->nRasterXSize, this->nRasterYSize,
549 : nBlockXSize, nBlockYSize, nTileNumber);
550 391 : if (eErr != CE_None)
551 0 : goto end;
552 :
553 841 : for (unsigned int iBand = 0; iBand < localctx.psImage->numcomps; iBand++)
554 : {
555 450 : if (localctx.psImage->comps[iBand].data == nullptr)
556 : {
557 0 : CPLError(CE_Failure, CPLE_AppDefined,
558 : "localctx.psImage->comps[%d].data == nullptr", iBand);
559 0 : eErr = CE_Failure;
560 0 : goto end;
561 : }
562 : }
563 :
564 841 : for (int xBand = 0; xBand < nBandCount; xBand++)
565 : {
566 450 : GDALRasterBlock *poBlock = nullptr;
567 450 : int iBand = (panBandMap) ? panBandMap[xBand] : xBand + 1;
568 450 : int bPromoteTo8Bit =
569 450 : (cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
570 : GetRasterBand(iBand)))
571 : ->bPromoteTo8Bit;
572 :
573 450 : void *pDstBuffer = nullptr;
574 450 : if (iBand == nBand)
575 391 : pDstBuffer = pImage;
576 : else
577 : {
578 59 : AcquireMutex();
579 59 : poBlock = (cpl::down_cast<JP2OPJLikeRasterBand<CODEC, BASE> *>(
580 : GetRasterBand(iBand)))
581 : ->TryGetLockedBlockRef(nBlockXOff, nBlockYOff);
582 59 : if (poBlock != nullptr)
583 : {
584 0 : ReleaseMutex();
585 0 : poBlock->DropLock();
586 0 : continue;
587 : }
588 :
589 59 : poBlock = GetRasterBand(iBand)->GetLockedBlockRef(nBlockXOff,
590 : nBlockYOff, TRUE);
591 59 : ReleaseMutex();
592 59 : if (poBlock == nullptr)
593 : {
594 0 : continue;
595 : }
596 :
597 59 : pDstBuffer = poBlock->GetDataRef();
598 : }
599 :
600 450 : if (this->bIs420)
601 : {
602 7 : if (static_cast<int>(localctx.psImage->comps[0].w) < nWidthToRead ||
603 7 : static_cast<int>(localctx.psImage->comps[0].h) <
604 7 : nHeightToRead ||
605 7 : localctx.psImage->comps[1].w !=
606 7 : (localctx.psImage->comps[0].w + 1) / 2 ||
607 7 : localctx.psImage->comps[1].h !=
608 7 : (localctx.psImage->comps[0].h + 1) / 2 ||
609 7 : localctx.psImage->comps[2].w !=
610 7 : (localctx.psImage->comps[0].w + 1) / 2 ||
611 7 : localctx.psImage->comps[2].h !=
612 7 : (localctx.psImage->comps[0].h + 1) / 2 ||
613 7 : (nBands == 4 &&
614 4 : (static_cast<int>(localctx.psImage->comps[3].w) <
615 4 : nWidthToRead ||
616 4 : static_cast<int>(localctx.psImage->comps[3].h) <
617 : nHeightToRead)))
618 : {
619 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
620 : "Assertion at line %d of %s failed", __LINE__,
621 : __FILE__);
622 0 : if (poBlock != nullptr)
623 0 : poBlock->DropLock();
624 0 : eErr = CE_Failure;
625 0 : goto end;
626 : }
627 :
628 7 : GByte *pDst = static_cast<GByte *>(pDstBuffer);
629 7 : if (iBand == 4)
630 : {
631 1 : const auto pSrcA =
632 1 : static_cast<int32_t *>(localctx.psImage->comps[3].data);
633 151 : for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
634 : {
635 300 : memcpy(pDst + j * nBlockXSize,
636 150 : pSrcA + j * localctx.stride(localctx.psImage->comps),
637 : nWidthToRead);
638 : }
639 : }
640 : else
641 : {
642 6 : const auto pSrcY =
643 6 : static_cast<int32_t *>(localctx.psImage->comps[0].data);
644 6 : const auto pSrcCb =
645 6 : static_cast<int32_t *>(localctx.psImage->comps[1].data);
646 6 : const auto pSrcCr =
647 6 : static_cast<int32_t *>(localctx.psImage->comps[2].data);
648 606 : for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
649 : {
650 81000 : for (int i = 0; i < nWidthToRead; i++)
651 : {
652 80400 : int Y =
653 80400 : pSrcY[j * localctx.stride(localctx.psImage->comps) +
654 80400 : i];
655 80400 : int Cb =
656 160800 : pSrcCb[(j / 2) * localctx.stride(
657 80400 : localctx.psImage->comps + 1) +
658 80400 : (i / 2)];
659 80400 : int Cr =
660 160800 : pSrcCr[(j / 2) * localctx.stride(
661 80400 : localctx.psImage->comps + 2) +
662 80400 : (i / 2)];
663 80400 : if (iBand == 1)
664 26800 : pDst[j * nBlockXSize + i] = CLAMP_0_255(
665 26800 : static_cast<int>(Y + 1.402 * (Cr - 128)));
666 53600 : else if (iBand == 2)
667 26800 : pDst[j * nBlockXSize + i] = CLAMP_0_255(
668 26800 : static_cast<int>(Y - 0.34414 * (Cb - 128) -
669 26800 : 0.71414 * (Cr - 128)));
670 26800 : else if (iBand == 3)
671 26800 : pDst[j * nBlockXSize + i] = CLAMP_0_255(
672 26800 : static_cast<int>(Y + 1.772 * (Cb - 128)));
673 : }
674 : }
675 : }
676 :
677 7 : if (bPromoteTo8Bit)
678 : {
679 0 : for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
680 : {
681 0 : for (int i = 0; i < nWidthToRead; i++)
682 : {
683 0 : pDst[j * nBlockXSize + i] *= 255;
684 : }
685 : }
686 : }
687 : }
688 : else
689 : {
690 443 : if (static_cast<int>(localctx.psImage->comps[iBand - 1].w) <
691 443 : nWidthToRead ||
692 443 : static_cast<int>(localctx.psImage->comps[iBand - 1].h) <
693 : nHeightToRead)
694 : {
695 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
696 : "Assertion at line %d of %s failed", __LINE__,
697 : __FILE__);
698 0 : if (poBlock != nullptr)
699 0 : poBlock->DropLock();
700 0 : eErr = CE_Failure;
701 0 : goto end;
702 : }
703 :
704 443 : auto src =
705 443 : static_cast<int32_t *>(localctx.psImage->comps[iBand - 1].data);
706 443 : if (bPromoteTo8Bit)
707 : {
708 529 : for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
709 : {
710 79500 : for (int i = 0; i < nWidthToRead; i++)
711 : {
712 157950 : src[j * localctx.stride(localctx.psImage->comps +
713 78975 : iBand - 1) +
714 78975 : i] *= 255;
715 : }
716 : }
717 : }
718 :
719 886 : if (static_cast<int>(localctx.stride(localctx.psImage->comps +
720 857 : iBand - 1)) == nBlockXSize &&
721 414 : static_cast<int>(localctx.psImage->comps[iBand - 1].h) ==
722 : nBlockYSize)
723 : {
724 393 : GDALCopyWords64(
725 : src, GDT_Int32, 4, pDstBuffer, eDataType, nDataTypeSize,
726 393 : static_cast<GPtrDiff_t>(nBlockXSize) * nBlockYSize);
727 : }
728 : else
729 : {
730 12249 : for (GPtrDiff_t j = 0; j < nHeightToRead; j++)
731 : {
732 24398 : GDALCopyWords(
733 12199 : src + j * localctx.stride(localctx.psImage->comps +
734 12199 : iBand - 1),
735 : GDT_Int32, 4,
736 : static_cast<GByte *>(pDstBuffer) +
737 12199 : j * nBlockXSize * nDataTypeSize,
738 : eDataType, nDataTypeSize, nWidthToRead);
739 : }
740 : }
741 : }
742 :
743 450 : if (poBlock != nullptr)
744 59 : poBlock->DropLock();
745 : }
746 :
747 391 : end:
748 391 : this->cache(&localctx);
749 :
750 782 : return eErr;
751 : }
752 :
753 : /************************************************************************/
754 : /* GetOverviewCount() */
755 : /************************************************************************/
756 :
757 : template <typename CODEC, typename BASE>
758 119 : int JP2OPJLikeRasterBand<CODEC, BASE>::GetOverviewCount()
759 : {
760 119 : auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
761 119 : if (!poGDS->AreOverviewsEnabled())
762 0 : return 0;
763 :
764 119 : if (GDALPamRasterBand::GetOverviewCount() > 0)
765 4 : return GDALPamRasterBand::GetOverviewCount();
766 :
767 115 : return poGDS->nOverviewCount;
768 : }
769 :
770 : /************************************************************************/
771 : /* GetOverview() */
772 : /************************************************************************/
773 :
774 : template <typename CODEC, typename BASE>
775 22 : GDALRasterBand *JP2OPJLikeRasterBand<CODEC, BASE>::GetOverview(int iOvrLevel)
776 : {
777 22 : if (GDALPamRasterBand::GetOverviewCount() > 0)
778 6 : return GDALPamRasterBand::GetOverview(iOvrLevel);
779 :
780 16 : auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
781 16 : if (iOvrLevel < 0 || iOvrLevel >= poGDS->nOverviewCount)
782 0 : return nullptr;
783 :
784 16 : return poGDS->papoOverviewDS[iOvrLevel]->GetRasterBand(nBand);
785 : }
786 :
787 : /************************************************************************/
788 : /* GetColorInterpretation() */
789 : /************************************************************************/
790 :
791 : template <typename CODEC, typename BASE>
792 570 : GDALColorInterp JP2OPJLikeRasterBand<CODEC, BASE>::GetColorInterpretation()
793 : {
794 570 : auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
795 :
796 570 : if (poCT)
797 6 : return GCI_PaletteIndex;
798 :
799 564 : if (nBand == poGDS->nAlphaIndex + 1)
800 22 : return GCI_AlphaBand;
801 :
802 949 : if (poGDS->nBands <= 2 &&
803 407 : poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY))
804 386 : return GCI_GrayIndex;
805 247 : else if (poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
806 91 : poGDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC))
807 : {
808 77 : if (nBand == poGDS->nRedIndex + 1)
809 26 : return GCI_RedBand;
810 51 : if (nBand == poGDS->nGreenIndex + 1)
811 25 : return GCI_GreenBand;
812 26 : if (nBand == poGDS->nBlueIndex + 1)
813 25 : return GCI_BlueBand;
814 : }
815 :
816 80 : return GCI_Undefined;
817 : }
818 :
819 : /************************************************************************/
820 : /* ==================================================================== */
821 : /* JP2OPJLikeDataset */
822 : /* ==================================================================== */
823 : /************************************************************************/
824 :
825 : /************************************************************************/
826 : /* JP2OPJLikeDataset() */
827 : /************************************************************************/
828 :
829 : template <typename CODEC, typename BASE>
830 1905 : JP2OPJLikeDataset<CODEC, BASE>::JP2OPJLikeDataset()
831 : {
832 1905 : this->init();
833 1905 : }
834 :
835 : /************************************************************************/
836 : /* ~JP2OPJLikeDataset() */
837 : /************************************************************************/
838 :
839 : template <typename CODEC, typename BASE>
840 2782 : JP2OPJLikeDataset<CODEC, BASE>::~JP2OPJLikeDataset()
841 :
842 : {
843 1905 : JP2OPJLikeDataset::Close();
844 2782 : }
845 :
846 : /************************************************************************/
847 : /* Close() */
848 : /************************************************************************/
849 :
850 : template <typename CODEC, typename BASE>
851 2598 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::Close(GDALProgressFunc, void *)
852 : {
853 2598 : CPLErr eErr = CE_None;
854 2598 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
855 : {
856 1905 : if (JP2OPJLikeDataset::FlushCache(true) != CE_None)
857 0 : eErr = CE_Failure;
858 :
859 1905 : this->closeJP2();
860 1905 : if (this->iLevel == 0 && this->fp_ != nullptr)
861 : {
862 760 : if (this->bRewrite)
863 : {
864 9 : if (BASE::canPerformDirectIO())
865 : {
866 : /* Grok handles box rewriting natively via transcode */
867 0 : VSIFCloseL(this->fp_);
868 0 : this->fp_ = nullptr;
869 0 : if (!CODEC::rewriteBoxes(GetDescription(), this))
870 0 : eErr = CE_Failure;
871 : }
872 : else
873 : {
874 18 : GDALJP2Box oBox(this->fp_);
875 9 : vsi_l_offset nOffsetJP2C = 0;
876 9 : vsi_l_offset nLengthJP2C = 0;
877 9 : vsi_l_offset nOffsetXML = 0;
878 9 : vsi_l_offset nOffsetASOC = 0;
879 9 : vsi_l_offset nOffsetUUID = 0;
880 9 : vsi_l_offset nOffsetIHDR = 0;
881 9 : vsi_l_offset nLengthIHDR = 0;
882 9 : int bMSIBox = FALSE;
883 9 : int bGMLData = FALSE;
884 9 : int bUnsupportedConfiguration = FALSE;
885 9 : if (oBox.ReadFirst())
886 : {
887 47 : while (strlen(oBox.GetType()) > 0)
888 : {
889 47 : if (EQUAL(oBox.GetType(), "jp2c"))
890 : {
891 9 : if (nOffsetJP2C == 0)
892 : {
893 9 : nOffsetJP2C = VSIFTellL(this->fp_);
894 9 : nLengthJP2C = oBox.GetDataLength();
895 : }
896 : else
897 0 : bUnsupportedConfiguration = TRUE;
898 : }
899 38 : else if (EQUAL(oBox.GetType(), "jp2h"))
900 : {
901 18 : GDALJP2Box oSubBox(this->fp_);
902 18 : if (oSubBox.ReadFirstChild(&oBox) &&
903 9 : EQUAL(oSubBox.GetType(), "ihdr"))
904 : {
905 9 : nOffsetIHDR = VSIFTellL(this->fp_);
906 9 : nLengthIHDR = oSubBox.GetDataLength();
907 : }
908 : }
909 29 : else if (EQUAL(oBox.GetType(), "xml "))
910 : {
911 2 : if (nOffsetXML == 0)
912 2 : nOffsetXML = VSIFTellL(this->fp_);
913 : }
914 27 : else if (EQUAL(oBox.GetType(), "asoc"))
915 : {
916 3 : if (nOffsetASOC == 0)
917 3 : nOffsetASOC = VSIFTellL(this->fp_);
918 :
919 6 : GDALJP2Box oSubBox(this->fp_);
920 6 : if (oSubBox.ReadFirstChild(&oBox) &&
921 3 : EQUAL(oSubBox.GetType(), "lbl "))
922 : {
923 : char *pszLabel = reinterpret_cast<char *>(
924 3 : oSubBox.ReadBoxData());
925 3 : if (pszLabel != nullptr &&
926 3 : EQUAL(pszLabel, "gml.data"))
927 : {
928 3 : bGMLData = TRUE;
929 : }
930 : else
931 0 : bUnsupportedConfiguration = TRUE;
932 3 : CPLFree(pszLabel);
933 : }
934 : else
935 0 : bUnsupportedConfiguration = TRUE;
936 : }
937 24 : else if (EQUAL(oBox.GetType(), "uuid"))
938 : {
939 4 : if (nOffsetUUID == 0)
940 4 : nOffsetUUID = VSIFTellL(this->fp_);
941 4 : if (GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()))
942 4 : bMSIBox = TRUE;
943 0 : else if (!GDALJP2Metadata::IsUUID_XMP(
944 : oBox.GetUUID()))
945 0 : bUnsupportedConfiguration = TRUE;
946 : }
947 20 : else if (!EQUAL(oBox.GetType(), "jP ") &&
948 11 : !EQUAL(oBox.GetType(), "ftyp") &&
949 2 : !EQUAL(oBox.GetType(), "rreq") &&
950 31 : !EQUAL(oBox.GetType(), "jp2h") &&
951 0 : !EQUAL(oBox.GetType(), "jp2i"))
952 : {
953 0 : bUnsupportedConfiguration = TRUE;
954 : }
955 :
956 47 : if (bUnsupportedConfiguration || !oBox.ReadNext())
957 9 : break;
958 : }
959 : }
960 :
961 : const char *pszGMLJP2;
962 9 : int bGeoreferencingCompatOfGMLJP2 =
963 13 : (!m_oSRS.IsEmpty() && bGeoTransformValid &&
964 4 : nGCPCount == 0);
965 9 : if (bGeoreferencingCompatOfGMLJP2 &&
966 3 : ((this->bHasGeoreferencingAtOpening && bGMLData) ||
967 2 : (!this->bHasGeoreferencingAtOpening)))
968 2 : pszGMLJP2 = "GMLJP2=YES";
969 : else
970 7 : pszGMLJP2 = "GMLJP2=NO";
971 :
972 : const char *pszGeoJP2;
973 9 : int bGeoreferencingCompatOfGeoJP2 =
974 12 : (!m_oSRS.IsEmpty() || nGCPCount != 0 ||
975 3 : bGeoTransformValid);
976 9 : if (bGeoreferencingCompatOfGeoJP2 &&
977 6 : ((this->bHasGeoreferencingAtOpening && bMSIBox) ||
978 4 : (!this->bHasGeoreferencingAtOpening) ||
979 2 : this->nGCPCount > 0))
980 5 : pszGeoJP2 = "GeoJP2=YES";
981 : else
982 4 : pszGeoJP2 = "GeoJP2=NO";
983 :
984 : /* Test that the length of the JP2C box is not 0 */
985 9 : int bJP2CBoxOKForRewriteInPlace = TRUE;
986 9 : if (nOffsetJP2C > 16 && !bUnsupportedConfiguration)
987 : {
988 9 : VSIFSeekL(this->fp_, nOffsetJP2C - 8, SEEK_SET);
989 : GByte abyBuffer[8];
990 9 : VSIFReadL(abyBuffer, 1, 8, this->fp_);
991 9 : if (memcmp(abyBuffer + 4, "jp2c", 4) == 0 &&
992 9 : abyBuffer[0] == 0 && abyBuffer[1] == 0 &&
993 9 : abyBuffer[2] == 0 && abyBuffer[3] == 0)
994 : {
995 1 : if (nLengthJP2C + 8 < UINT32_MAX)
996 : {
997 1 : CPLDebug(CODEC::debugId(),
998 : "Patching length of JP2C box with "
999 : "real length");
1000 1 : VSIFSeekL(this->fp_, nOffsetJP2C - 8, SEEK_SET);
1001 1 : GUInt32 nLength =
1002 1 : static_cast<GUInt32>(nLengthJP2C) + 8;
1003 1 : CPL_MSBPTR32(&nLength);
1004 1 : if (VSIFWriteL(&nLength, 1, 4, this->fp_) != 1)
1005 1 : eErr = CE_Failure;
1006 : }
1007 : else
1008 0 : bJP2CBoxOKForRewriteInPlace = FALSE;
1009 : }
1010 : }
1011 :
1012 9 : if (nOffsetJP2C == 0 || bUnsupportedConfiguration)
1013 : {
1014 0 : eErr = CE_Failure;
1015 0 : CPLError(
1016 : CE_Failure, CPLE_AppDefined,
1017 : "Cannot rewrite file due to unsupported JP2 box "
1018 : "configuration");
1019 0 : VSIFCloseL(this->fp_);
1020 : }
1021 9 : else if (bJP2CBoxOKForRewriteInPlace &&
1022 9 : (nOffsetXML == 0 || nOffsetXML > nOffsetJP2C) &&
1023 9 : (nOffsetASOC == 0 || nOffsetASOC > nOffsetJP2C) &&
1024 4 : (nOffsetUUID == 0 || nOffsetUUID > nOffsetJP2C))
1025 : {
1026 5 : CPLDebug(CODEC::debugId(),
1027 : "Rewriting boxes after codestream");
1028 :
1029 : /* Update IPR flag */
1030 5 : if (nLengthIHDR == 14)
1031 : {
1032 5 : VSIFSeekL(this->fp_, nOffsetIHDR + nLengthIHDR - 1,
1033 : SEEK_SET);
1034 5 : GByte bIPR = GetMetadata("xml:IPR") != nullptr;
1035 5 : if (VSIFWriteL(&bIPR, 1, 1, this->fp_) != 1)
1036 0 : eErr = CE_Failure;
1037 : }
1038 :
1039 5 : VSIFSeekL(this->fp_, nOffsetJP2C + nLengthJP2C,
1040 : SEEK_SET);
1041 :
1042 10 : GDALJP2Metadata oJP2MD;
1043 5 : if (GetGCPCount() > 0)
1044 : {
1045 1 : oJP2MD.SetGCPs(GetGCPCount(), GetGCPs());
1046 1 : oJP2MD.SetSpatialRef(GetGCPSpatialRef());
1047 : }
1048 : else
1049 : {
1050 4 : const OGRSpatialReference *poSRS = GetSpatialRef();
1051 4 : if (poSRS != nullptr)
1052 : {
1053 1 : oJP2MD.SetSpatialRef(poSRS);
1054 : }
1055 4 : if (bGeoTransformValid)
1056 : {
1057 1 : oJP2MD.SetGeoTransform(m_gt);
1058 : }
1059 : }
1060 :
1061 : const char *pszAreaOrPoint =
1062 5 : GetMetadataItem(GDALMD_AREA_OR_POINT);
1063 5 : oJP2MD.bPixelIsPoint =
1064 5 : pszAreaOrPoint != nullptr &&
1065 0 : EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
1066 :
1067 5 : if (!WriteIPRBox(this->fp_, this))
1068 0 : eErr = CE_Failure;
1069 :
1070 5 : if (bGeoreferencingCompatOfGMLJP2 &&
1071 1 : EQUAL(pszGMLJP2, "GMLJP2=YES"))
1072 : {
1073 : GDALJP2Box *poBox =
1074 1 : oJP2MD.CreateGMLJP2(nRasterXSize, nRasterYSize);
1075 1 : if (!WriteBox(this->fp_, poBox))
1076 0 : eErr = CE_Failure;
1077 1 : delete poBox;
1078 : }
1079 :
1080 10 : if (!WriteXMLBoxes(this->fp_, this) ||
1081 5 : !WriteGDALMetadataBox(this->fp_, this, nullptr))
1082 0 : eErr = CE_Failure;
1083 :
1084 5 : if (bGeoreferencingCompatOfGeoJP2 &&
1085 2 : EQUAL(pszGeoJP2, "GeoJP2=YES"))
1086 : {
1087 2 : GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
1088 2 : if (!WriteBox(this->fp_, poBox))
1089 0 : eErr = CE_Failure;
1090 2 : delete poBox;
1091 : }
1092 :
1093 5 : if (!WriteXMPBox(this->fp_, this))
1094 0 : eErr = CE_Failure;
1095 :
1096 5 : if (VSIFTruncateL(this->fp_, VSIFTellL(this->fp_)) != 0)
1097 0 : eErr = CE_Failure;
1098 :
1099 5 : if (VSIFCloseL(this->fp_) != 0)
1100 5 : eErr = CE_Failure;
1101 : }
1102 : else
1103 : {
1104 4 : VSIFCloseL(this->fp_);
1105 :
1106 4 : CPLDebug(CODEC::debugId(), "Rewriting whole file");
1107 :
1108 4 : const char *const apszOptions[] = {
1109 : "USE_SRC_CODESTREAM=YES",
1110 : "CODEC=JP2",
1111 : "WRITE_METADATA=YES",
1112 : pszGMLJP2,
1113 : pszGeoJP2,
1114 : nullptr};
1115 8 : CPLString osTmpFilename(
1116 4 : CPLSPrintf("%s.tmp", GetDescription()));
1117 : GDALDataset *poOutDS =
1118 4 : CreateCopy(osTmpFilename, this, FALSE,
1119 : const_cast<char **>(apszOptions),
1120 : GDALDummyProgress, nullptr);
1121 4 : if (poOutDS)
1122 : {
1123 4 : if (GDALClose(poOutDS) != CE_None)
1124 0 : eErr = CE_Failure;
1125 4 : if (VSIRename(osTmpFilename, GetDescription()) != 0)
1126 0 : eErr = CE_Failure;
1127 : }
1128 : else
1129 : {
1130 0 : eErr = CE_Failure;
1131 0 : VSIUnlink(osTmpFilename);
1132 : }
1133 4 : VSIUnlink(
1134 4 : CPLSPrintf("%s.tmp.aux.xml", GetDescription()));
1135 : }
1136 : }
1137 : }
1138 : else
1139 751 : VSIFCloseL(this->fp_);
1140 : }
1141 :
1142 1905 : JP2OPJLikeDataset::CloseDependentDatasets();
1143 :
1144 1905 : if (GDALPamDataset::Close() != CE_None)
1145 0 : eErr = CE_Failure;
1146 : }
1147 2598 : return eErr;
1148 : }
1149 :
1150 : /************************************************************************/
1151 : /* CloseDependentDatasets() */
1152 : /************************************************************************/
1153 :
1154 : template <typename CODEC, typename BASE>
1155 1919 : int JP2OPJLikeDataset<CODEC, BASE>::CloseDependentDatasets()
1156 : {
1157 1919 : int bRet = GDALJP2AbstractDataset::CloseDependentDatasets();
1158 1919 : if (papoOverviewDS)
1159 : {
1160 178 : for (int i = 0; i < this->nOverviewCount; i++)
1161 115 : delete papoOverviewDS[i];
1162 63 : CPLFree(papoOverviewDS);
1163 63 : papoOverviewDS = nullptr;
1164 63 : bRet = TRUE;
1165 : }
1166 1919 : return bRet;
1167 : }
1168 :
1169 : /************************************************************************/
1170 : /* SetSpatialRef() */
1171 : /************************************************************************/
1172 :
1173 : template <typename CODEC, typename BASE>
1174 : CPLErr
1175 18 : JP2OPJLikeDataset<CODEC, BASE>::SetSpatialRef(const OGRSpatialReference *poSRS)
1176 : {
1177 18 : if (eAccess == GA_Update)
1178 : {
1179 2 : this->bRewrite = TRUE;
1180 2 : m_oSRS.Clear();
1181 2 : if (poSRS)
1182 1 : m_oSRS = *poSRS;
1183 2 : return CE_None;
1184 : }
1185 : else
1186 16 : return GDALJP2AbstractDataset::SetSpatialRef(poSRS);
1187 : }
1188 :
1189 : /************************************************************************/
1190 : /* SetGeoTransform() */
1191 : /************************************************************************/
1192 :
1193 : template <typename CODEC, typename BASE>
1194 : CPLErr
1195 24 : JP2OPJLikeDataset<CODEC, BASE>::SetGeoTransform(const GDALGeoTransform >)
1196 : {
1197 24 : if (eAccess == GA_Update)
1198 : {
1199 4 : this->bRewrite = TRUE;
1200 4 : m_gt = gt;
1201 4 : bGeoTransformValid = m_gt != GDALGeoTransform();
1202 4 : return CE_None;
1203 : }
1204 : else
1205 20 : return GDALJP2AbstractDataset::SetGeoTransform(gt);
1206 : }
1207 :
1208 : /************************************************************************/
1209 : /* SetGCPs() */
1210 : /************************************************************************/
1211 :
1212 : template <typename CODEC, typename BASE>
1213 4 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetGCPs(int nGCPCountIn,
1214 : const GDAL_GCP *pasGCPListIn,
1215 : const OGRSpatialReference *poSRS)
1216 : {
1217 4 : if (eAccess == GA_Update)
1218 : {
1219 3 : this->bRewrite = TRUE;
1220 3 : if (nGCPCount > 0)
1221 : {
1222 1 : GDALDeinitGCPs(nGCPCount, pasGCPList);
1223 1 : CPLFree(pasGCPList);
1224 : }
1225 :
1226 3 : m_oSRS.Clear();
1227 3 : if (poSRS)
1228 2 : m_oSRS = *poSRS;
1229 :
1230 3 : nGCPCount = nGCPCountIn;
1231 3 : pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
1232 :
1233 3 : return CE_None;
1234 : }
1235 : else
1236 1 : return GDALJP2AbstractDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
1237 1 : poSRS);
1238 : }
1239 :
1240 : /************************************************************************/
1241 : /* SetMetadata() */
1242 : /************************************************************************/
1243 :
1244 : template <typename CODEC, typename BASE>
1245 11 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetMetadata(CSLConstList papszMetadata,
1246 : const char *pszDomain)
1247 : {
1248 11 : if (eAccess == GA_Update)
1249 : {
1250 2 : this->bRewrite = TRUE;
1251 2 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
1252 : {
1253 1 : CSLDestroy(m_papszMainMD);
1254 1 : m_papszMainMD = CSLDuplicate(papszMetadata);
1255 : }
1256 2 : return GDALDataset::SetMetadata(papszMetadata, pszDomain);
1257 : }
1258 9 : return GDALJP2AbstractDataset::SetMetadata(papszMetadata, pszDomain);
1259 : }
1260 :
1261 : /************************************************************************/
1262 : /* SetMetadata() */
1263 : /************************************************************************/
1264 :
1265 : template <typename CODEC, typename BASE>
1266 3 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetMetadataItem(const char *pszName,
1267 : const char *pszValue,
1268 : const char *pszDomain)
1269 : {
1270 3 : if (eAccess == GA_Update)
1271 : {
1272 1 : this->bRewrite = TRUE;
1273 1 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
1274 : {
1275 1 : GetMetadata(); // update m_papszMainMD
1276 1 : m_papszMainMD = CSLSetNameValue(m_papszMainMD, pszName, pszValue);
1277 : }
1278 1 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1279 : }
1280 2 : return GDALJP2AbstractDataset::SetMetadataItem(pszName, pszValue,
1281 2 : pszDomain);
1282 : }
1283 :
1284 : /************************************************************************/
1285 : /* Identify() */
1286 : /************************************************************************/
1287 :
1288 : #ifndef jpc_header_defined
1289 : #define jpc_header_defined
1290 : static const unsigned char jpc_header[] = {0xff, 0x4f, 0xff,
1291 : 0x51}; // SOC + RSIZ markers
1292 : static const unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP ' */
1293 : #endif
1294 :
1295 : template <typename CODEC, typename BASE>
1296 767 : int JP2OPJLikeDataset<CODEC, BASE>::Identify(GDALOpenInfo *poOpenInfo)
1297 :
1298 : {
1299 767 : if (poOpenInfo->nHeaderBytes >= 16 &&
1300 767 : (memcmp(poOpenInfo->pabyHeader, jpc_header, sizeof(jpc_header)) == 0 ||
1301 684 : memcmp(poOpenInfo->pabyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) ==
1302 : 0))
1303 767 : return TRUE;
1304 :
1305 : else
1306 0 : return FALSE;
1307 : }
1308 :
1309 : /************************************************************************/
1310 : /* JP2FindCodeStream() */
1311 : /************************************************************************/
1312 :
1313 : template <typename CODEC, typename BASE>
1314 : vsi_l_offset
1315 774 : JP2OPJLikeDataset<CODEC, BASE>::JP2FindCodeStream(VSILFILE *fp,
1316 : vsi_l_offset *pnLength)
1317 : {
1318 774 : vsi_l_offset nCodeStreamStart = 0;
1319 774 : vsi_l_offset nCodeStreamLength = 0;
1320 :
1321 774 : VSIFSeekL(fp, 0, SEEK_SET);
1322 : GByte abyHeader[16];
1323 774 : VSIFReadL(abyHeader, 1, 16, fp);
1324 :
1325 774 : if (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0)
1326 : {
1327 83 : VSIFSeekL(fp, 0, SEEK_END);
1328 83 : nCodeStreamLength = VSIFTellL(fp);
1329 83 : VSIFSeekL(fp, 0, SEEK_SET);
1330 : }
1331 691 : else if (memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) == 0)
1332 : {
1333 690 : if (BASE::canPerformDirectIO())
1334 : {
1335 : // Grok reads the full JP2 file (including boxes) natively,
1336 : // so pass nCodeStreamStart=0 and the full file length.
1337 : // JP2 boxes (cdef, pclr, etc.) are parsed by Grok's own
1338 : // JP2 family decoder.
1339 0 : VSIFSeekL(fp, 0, SEEK_END);
1340 0 : nCodeStreamLength = VSIFTellL(fp);
1341 0 : VSIFSeekL(fp, 0, SEEK_SET);
1342 : }
1343 : else
1344 : {
1345 : /* Find offset of first jp2c box */
1346 1380 : GDALJP2Box oBox(fp);
1347 690 : if (oBox.ReadFirst())
1348 : {
1349 3589 : while (strlen(oBox.GetType()) > 0)
1350 : {
1351 3589 : if (EQUAL(oBox.GetType(), "jp2c"))
1352 : {
1353 689 : nCodeStreamStart = VSIFTellL(fp);
1354 689 : nCodeStreamLength = oBox.GetDataLength();
1355 689 : break;
1356 : }
1357 :
1358 2900 : if (!oBox.ReadNext())
1359 1 : break;
1360 : }
1361 : }
1362 : }
1363 : }
1364 : else
1365 : {
1366 1 : CPLError(CE_Failure, CPLE_AppDefined, "No JPEG 2000 stream detected");
1367 : }
1368 :
1369 774 : *pnLength = nCodeStreamLength;
1370 774 : return nCodeStreamStart;
1371 : }
1372 :
1373 : /************************************************************************/
1374 : /* Open() */
1375 : /************************************************************************/
1376 :
1377 : template <typename CODEC, typename BASE>
1378 767 : GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::Open(GDALOpenInfo *poOpenInfo)
1379 :
1380 : {
1381 767 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
1382 0 : return nullptr;
1383 :
1384 : /* Detect which codec to use : J2K or JP2 ? */
1385 767 : vsi_l_offset nCodeStreamLength = 0;
1386 : vsi_l_offset nCodeStreamStart =
1387 767 : JP2FindCodeStream(poOpenInfo->fpL, &nCodeStreamLength);
1388 :
1389 767 : if (nCodeStreamStart == 0 && nCodeStreamLength == 0)
1390 : {
1391 1 : CPLError(CE_Failure, CPLE_AppDefined, "No code-stream in JP2 file");
1392 1 : return nullptr;
1393 : }
1394 1532 : JP2OPJLikeDataset oTmpDS;
1395 766 : int numThreads = oTmpDS.GetNumThreads();
1396 1532 : auto eCodecFormat = (memcmp(poOpenInfo->pabyHeader + 4, jp2_box_jp,
1397 : sizeof(jp2_box_jp)) == 0)
1398 766 : ? CODEC::cvtenum(JP2_CODEC_JP2)
1399 83 : : CODEC::cvtenum(JP2_CODEC_J2K);
1400 :
1401 766 : uint32_t nTileW = 0, nTileH = 0;
1402 766 : int numResolutions = 0;
1403 766 : CODEC localctx;
1404 766 : localctx.open(poOpenInfo->fpL, nCodeStreamStart);
1405 766 : if (!localctx.setUpDecompress(numThreads, poOpenInfo->pszFilename,
1406 : nCodeStreamLength, &nTileW, &nTileH,
1407 : &numResolutions))
1408 6 : return nullptr;
1409 :
1410 760 : GDALDataType eDataType = GDT_UInt8;
1411 760 : if (localctx.psImage->comps[0].prec > 16)
1412 : {
1413 0 : if (localctx.psImage->comps[0].sgnd)
1414 0 : eDataType = GDT_Int32;
1415 : else
1416 0 : eDataType = GDT_UInt32;
1417 : }
1418 760 : else if (localctx.psImage->comps[0].prec > 8)
1419 : {
1420 26 : if (localctx.psImage->comps[0].sgnd)
1421 7 : eDataType = GDT_Int16;
1422 : else
1423 19 : eDataType = GDT_UInt16;
1424 : }
1425 :
1426 760 : int bIs420 =
1427 1520 : (localctx.psImage->color_space != CODEC::cvtenum(JP2_CLRSPC_SRGB) &&
1428 734 : eDataType == GDT_UInt8 &&
1429 734 : (localctx.psImage->numcomps == 3 || localctx.psImage->numcomps == 4) &&
1430 59 : localctx.psImage->comps[1].w == localctx.psImage->comps[0].w / 2 &&
1431 3 : localctx.psImage->comps[1].h == localctx.psImage->comps[0].h / 2 &&
1432 3 : localctx.psImage->comps[2].w == localctx.psImage->comps[0].w / 2 &&
1433 1523 : localctx.psImage->comps[2].h == localctx.psImage->comps[0].h / 2) &&
1434 3 : (localctx.psImage->numcomps == 3 ||
1435 2 : (localctx.psImage->numcomps == 4 &&
1436 2 : localctx.psImage->comps[3].w == localctx.psImage->comps[0].w &&
1437 2 : localctx.psImage->comps[3].h == localctx.psImage->comps[0].h));
1438 :
1439 760 : if (bIs420)
1440 : {
1441 3 : CPLDebug(CODEC::debugId(), "420 format");
1442 : }
1443 : else
1444 : {
1445 921 : for (unsigned iBand = 2; iBand <= localctx.psImage->numcomps; iBand++)
1446 : {
1447 164 : if (localctx.psImage->comps[iBand - 1].w !=
1448 164 : localctx.psImage->comps[0].w ||
1449 164 : localctx.psImage->comps[iBand - 1].h !=
1450 164 : localctx.psImage->comps[0].h)
1451 : {
1452 0 : CPLDebug(CODEC::debugId(), "Unable to handle that image (2)");
1453 0 : localctx.free();
1454 0 : return nullptr;
1455 : }
1456 : }
1457 : }
1458 :
1459 : /* -------------------------------------------------------------------- */
1460 : /* Create a corresponding GDALDataset. */
1461 : /* -------------------------------------------------------------------- */
1462 : JP2OPJLikeDataset *poDS;
1463 : int iBand;
1464 :
1465 760 : poDS = new JP2OPJLikeDataset();
1466 760 : poDS->m_osFilename = poOpenInfo->pszFilename;
1467 760 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
1468 679 : poDS->eAccess = poOpenInfo->eAccess;
1469 760 : poDS->eColorSpace = localctx.psImage->color_space;
1470 760 : poDS->nRasterXSize = localctx.psImage->x1 - localctx.psImage->x0;
1471 760 : poDS->nRasterYSize = localctx.psImage->y1 - localctx.psImage->y0;
1472 760 : poDS->nBands = localctx.psImage->numcomps;
1473 760 : poDS->fp_ = poOpenInfo->fpL;
1474 760 : poOpenInfo->fpL = nullptr;
1475 760 : poDS->nCodeStreamStart = nCodeStreamStart;
1476 760 : poDS->nCodeStreamLength = nCodeStreamLength;
1477 760 : poDS->bIs420 = bIs420;
1478 1470 : poDS->bSingleTiled = (poDS->nRasterXSize == static_cast<int>(nTileW) &&
1479 710 : poDS->nRasterYSize == static_cast<int>(nTileH));
1480 760 : poDS->m_nX0 = localctx.psImage->x0;
1481 760 : poDS->m_nY0 = localctx.psImage->y0;
1482 760 : poDS->m_nTileWidth = nTileW;
1483 760 : poDS->m_nTileHeight = nTileH;
1484 :
1485 760 : int nBlockXSize = static_cast<int>(nTileW);
1486 760 : int nBlockYSize = static_cast<int>(nTileH);
1487 :
1488 760 : if (CPLFetchBool(poOpenInfo->papszOpenOptions, "USE_TILE_AS_BLOCK", false))
1489 : {
1490 0 : poDS->bUseSetDecodeArea = false;
1491 : }
1492 :
1493 760 : poDS->m_bStrict = CPLTestBool(
1494 760 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "STRICT", "YES"));
1495 760 : localctx.updateStrict(poDS->m_bStrict);
1496 :
1497 760 : if (localctx.preferPerBlockDeCompress())
1498 : {
1499 : /* Some Sentinel2 preview datasets are 343x343 large, but with 8x8 blocks */
1500 : /* Using the tile API for that is super slow, so expose a single block */
1501 760 : if (poDS->nRasterXSize <= 1024 && poDS->nRasterYSize <= 1024 &&
1502 744 : nTileW < 32 && nTileH < 32)
1503 : {
1504 575 : poDS->bUseSetDecodeArea = true;
1505 575 : nBlockXSize = poDS->nRasterXSize;
1506 575 : nBlockYSize = poDS->nRasterYSize;
1507 : }
1508 : else
1509 : {
1510 185 : poDS->bUseSetDecodeArea =
1511 322 : poDS->bSingleTiled &&
1512 137 : (poDS->nRasterXSize > 1024 || poDS->nRasterYSize > 1024);
1513 :
1514 : /* Other Sentinel2 preview datasets are 343x343 and 60m are 1830x1830,
1515 : * but they */
1516 : /* are tiled with tile dimensions 2048x2048. It would be a waste of */
1517 : /* memory to allocate such big blocks */
1518 185 : if (poDS->nRasterXSize < static_cast<int>(nTileW) &&
1519 18 : poDS->nRasterYSize < static_cast<int>(nTileH))
1520 : {
1521 18 : poDS->bUseSetDecodeArea = TRUE;
1522 18 : nBlockXSize = poDS->nRasterXSize;
1523 18 : nBlockYSize = poDS->nRasterYSize;
1524 18 : if (nBlockXSize > 2048)
1525 0 : nBlockXSize = 2048;
1526 18 : if (nBlockYSize > 2048)
1527 0 : nBlockYSize = 2048;
1528 : }
1529 167 : else if (poDS->bUseSetDecodeArea)
1530 : {
1531 : // Arbitrary threshold... ~4 million at least needed for the GRIB2
1532 : // images mentioned below.
1533 7 : if (nTileH == 1 && nTileW < 20 * 1024 * 1024)
1534 : {
1535 : // Some GRIB2 JPEG2000 compressed images are a 2D image
1536 : // organized as a single line image...
1537 : }
1538 : else
1539 : {
1540 7 : if (nBlockXSize > 1024)
1541 7 : nBlockXSize = 1024;
1542 7 : if (nBlockYSize > 1024)
1543 7 : nBlockYSize = 1024;
1544 : }
1545 : }
1546 : }
1547 : }
1548 :
1549 760 : GDALColorTable *poCT = nullptr;
1550 :
1551 : /* -------------------------------------------------------------------- */
1552 : /* Look for color table or cdef box */
1553 : /* -------------------------------------------------------------------- */
1554 1439 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
1555 679 : BASE::canPerformDirectIO())
1556 : {
1557 : /* Grok parses JP2 boxes natively; extract cdef/pclr from codec */
1558 0 : localctx.extractJP2BoxInfo(poDS->nBands, poDS->nRedIndex,
1559 0 : poDS->nGreenIndex, poDS->nBlueIndex,
1560 0 : poDS->nAlphaIndex, &poCT);
1561 : }
1562 760 : else if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
1563 : {
1564 679 : vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
1565 :
1566 1358 : GDALJP2Box oBox(poDS->fp_);
1567 679 : if (oBox.ReadFirst())
1568 : {
1569 3629 : while (strlen(oBox.GetType()) > 0)
1570 : {
1571 3629 : if (EQUAL(oBox.GetType(), "jp2h"))
1572 : {
1573 1358 : GDALJP2Box oSubBox(poDS->fp_);
1574 :
1575 2150 : for (oSubBox.ReadFirstChild(&oBox);
1576 2150 : strlen(oSubBox.GetType()) > 0;
1577 1471 : oSubBox.ReadNextChild(&oBox))
1578 : {
1579 1471 : GIntBig nDataLength = oSubBox.GetDataLength();
1580 4388 : if (poCT == nullptr &&
1581 1446 : EQUAL(oSubBox.GetType(), "pclr") &&
1582 2917 : nDataLength >= 3 &&
1583 : nDataLength <= 2 + 1 + 4 + 4 * 256)
1584 : {
1585 24 : GByte *pabyCT = oSubBox.ReadBoxData();
1586 24 : if (pabyCT != nullptr)
1587 : {
1588 24 : int nEntries = (pabyCT[0] << 8) | pabyCT[1];
1589 24 : int nComponents = pabyCT[2];
1590 : /* CPLDebug(CODEC::debugId(), "Color table found"); */
1591 24 : if (nEntries <= 256 && nComponents == 3)
1592 : {
1593 : /*CPLDebug(CODEC::debugId(), "resol[0] = %d",
1594 : pabyCT[3]); CPLDebug(CODEC::debugId(), "resol[1] =
1595 : %d", pabyCT[4]); CPLDebug(CODEC::debugId(),
1596 : "resol[2] = %d", pabyCT[5]);*/
1597 19 : if (pabyCT[3] == 7 && pabyCT[4] == 7 &&
1598 19 : pabyCT[5] == 7 &&
1599 19 : nDataLength == 2 + 1 + 3 + 3 * nEntries)
1600 : {
1601 19 : poCT = new GDALColorTable();
1602 1103 : for (int i = 0; i < nEntries; i++)
1603 : {
1604 : GDALColorEntry sEntry;
1605 1084 : sEntry.c1 = pabyCT[6 + 3 * i];
1606 1084 : sEntry.c2 = pabyCT[6 + 3 * i + 1];
1607 1084 : sEntry.c3 = pabyCT[6 + 3 * i + 2];
1608 1084 : sEntry.c4 = 255;
1609 1084 : poCT->SetColorEntry(i, &sEntry);
1610 : }
1611 19 : }
1612 : }
1613 5 : else if (nEntries <= 256 && nComponents == 4)
1614 : {
1615 5 : if (pabyCT[3] == 7 && pabyCT[4] == 7 &&
1616 5 : pabyCT[5] == 7 && pabyCT[6] == 7 &&
1617 5 : nDataLength == 2 + 1 + 4 + 4 * nEntries)
1618 : {
1619 5 : poCT = new GDALColorTable();
1620 17 : for (int i = 0; i < nEntries; i++)
1621 : {
1622 : GDALColorEntry sEntry;
1623 12 : sEntry.c1 = pabyCT[7 + 4 * i];
1624 12 : sEntry.c2 = pabyCT[7 + 4 * i + 1];
1625 12 : sEntry.c3 = pabyCT[7 + 4 * i + 2];
1626 12 : sEntry.c4 = pabyCT[7 + 4 * i + 3];
1627 12 : poCT->SetColorEntry(i, &sEntry);
1628 : }
1629 : }
1630 : }
1631 24 : CPLFree(pabyCT);
1632 : }
1633 : }
1634 : /* There's a bug/misfeature in openjpeg: the color_space
1635 : only gets set at read tile time */
1636 1447 : else if (EQUAL(oSubBox.GetType(), "colr") &&
1637 : nDataLength == 7)
1638 : {
1639 679 : GByte *pabyContent = oSubBox.ReadBoxData();
1640 679 : if (pabyContent != nullptr)
1641 : {
1642 679 : if (pabyContent[0] ==
1643 : 1 /* enumerated colourspace */)
1644 : {
1645 679 : GUInt32 enumcs = (pabyContent[3] << 24) |
1646 679 : (pabyContent[4] << 16) |
1647 679 : (pabyContent[5] << 8) |
1648 : (pabyContent[6]);
1649 679 : if (enumcs == 16)
1650 : {
1651 53 : poDS->eColorSpace =
1652 53 : CODEC::cvtenum(JP2_CLRSPC_SRGB);
1653 53 : CPLDebug(CODEC::debugId(),
1654 : "SRGB color space");
1655 : }
1656 626 : else if (enumcs == 17)
1657 : {
1658 619 : poDS->eColorSpace =
1659 619 : CODEC::cvtenum(JP2_CLRSPC_GRAY);
1660 619 : CPLDebug(CODEC::debugId(),
1661 : "Grayscale color space");
1662 : }
1663 7 : else if (enumcs == 18)
1664 : {
1665 3 : poDS->eColorSpace =
1666 3 : CODEC::cvtenum(JP2_CLRSPC_SYCC);
1667 3 : CPLDebug(CODEC::debugId(),
1668 : "SYCC color space");
1669 : }
1670 4 : else if (enumcs == 20)
1671 : {
1672 : /* Used by
1673 : * J2KP4files/testfiles_jp2/file7.jp2 */
1674 0 : poDS->eColorSpace =
1675 0 : CODEC::cvtenum(JP2_CLRSPC_SRGB);
1676 0 : CPLDebug(CODEC::debugId(),
1677 : "e-sRGB color space");
1678 : }
1679 4 : else if (enumcs == 21)
1680 : {
1681 : /* Used by
1682 : * J2KP4files/testfiles_jp2/file5.jp2 */
1683 0 : poDS->eColorSpace =
1684 0 : CODEC::cvtenum(JP2_CLRSPC_SRGB);
1685 0 : CPLDebug(CODEC::debugId(),
1686 : "ROMM-RGB color space");
1687 : }
1688 : else
1689 : {
1690 4 : poDS->eColorSpace =
1691 4 : CODEC::cvtenum(JP2_CLRSPC_UNKNOWN);
1692 4 : CPLDebug(CODEC::debugId(),
1693 : "Unknown color space");
1694 : }
1695 : }
1696 679 : CPLFree(pabyContent);
1697 : }
1698 : }
1699 : /* Check if there's an alpha channel or odd channel
1700 : * attribution */
1701 799 : else if (EQUAL(oSubBox.GetType(), "cdef") &&
1702 31 : nDataLength == 2 + poDS->nBands * 6)
1703 : {
1704 30 : GByte *pabyContent = oSubBox.ReadBoxData();
1705 30 : if (pabyContent != nullptr)
1706 : {
1707 30 : int nEntries =
1708 30 : (pabyContent[0] << 8) | pabyContent[1];
1709 30 : if (nEntries == poDS->nBands)
1710 : {
1711 30 : poDS->nRedIndex = -1;
1712 30 : poDS->nGreenIndex = -1;
1713 30 : poDS->nBlueIndex = -1;
1714 135 : for (int i = 0; i < poDS->nBands; i++)
1715 : {
1716 105 : int CNi =
1717 105 : (pabyContent[2 + 6 * i] << 8) |
1718 105 : pabyContent[2 + 6 * i + 1];
1719 105 : int Typi =
1720 105 : (pabyContent[2 + 6 * i + 2] << 8) |
1721 105 : pabyContent[2 + 6 * i + 3];
1722 105 : int Asoci =
1723 105 : (pabyContent[2 + 6 * i + 4] << 8) |
1724 105 : pabyContent[2 + 6 * i + 5];
1725 105 : if (CNi < 0 || CNi >= poDS->nBands)
1726 : {
1727 0 : CPLError(CE_Failure,
1728 : CPLE_AppDefined,
1729 : "Wrong value of CN%d=%d",
1730 : i, CNi);
1731 0 : break;
1732 : }
1733 105 : if (Typi == 0)
1734 : {
1735 66 : if (Asoci == 1)
1736 30 : poDS->nRedIndex = CNi;
1737 36 : else if (Asoci == 2)
1738 18 : poDS->nGreenIndex = CNi;
1739 18 : else if (Asoci == 3)
1740 18 : poDS->nBlueIndex = CNi;
1741 0 : else if (Asoci < 0 ||
1742 0 : (Asoci > poDS->nBands &&
1743 : Asoci != 65535))
1744 : {
1745 0 : CPLError(
1746 : CE_Failure, CPLE_AppDefined,
1747 : "Wrong value of Asoc%d=%d",
1748 : i, Asoci);
1749 0 : break;
1750 : }
1751 : }
1752 39 : else if (Typi == 1)
1753 : {
1754 27 : poDS->nAlphaIndex = CNi;
1755 : }
1756 : }
1757 : }
1758 : else
1759 : {
1760 0 : CPLDebug(CODEC::debugId(),
1761 : "Unsupported cdef content");
1762 : }
1763 30 : CPLFree(pabyContent);
1764 : }
1765 : }
1766 : }
1767 : }
1768 :
1769 3629 : if (!oBox.ReadNext())
1770 679 : break;
1771 : }
1772 : }
1773 :
1774 679 : VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
1775 : }
1776 :
1777 1439 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
1778 679 : poDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
1779 619 : poDS->nBands == 4 && poDS->nRedIndex == 0 && poDS->nGreenIndex == 1 &&
1780 1444 : poDS->nBlueIndex == 2 &&
1781 5 : poDS->m_osFilename.find("dop10rgbi") != std::string::npos)
1782 : {
1783 0 : CPLDebug(CODEC::debugId(),
1784 : "Autofix wrong colorspace from Greyscale to sRGB");
1785 : // Workaround https://github.com/uclouvain/openjpeg/issues/1464
1786 : // dop10rgbi products from https://www.opengeodata.nrw.de/produkte/geobasis/lusat/dop/dop_jp2_f10/
1787 : // have a wrong color space.
1788 0 : poDS->eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
1789 : }
1790 :
1791 : /* -------------------------------------------------------------------- */
1792 : /* Create band information objects. */
1793 : /* -------------------------------------------------------------------- */
1794 1692 : for (iBand = 1; iBand <= poDS->nBands; iBand++)
1795 : {
1796 932 : const bool bPromoteTo8Bit =
1797 959 : iBand == poDS->nAlphaIndex + 1 &&
1798 27 : localctx.psImage
1799 27 : ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1) ? 1
1800 : : 0]
1801 27 : .prec == 8 &&
1802 973 : localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
1803 14 : CPLFetchBool(poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
1804 14 : CPLTestBool(CPLGetConfigOption(
1805 : "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
1806 932 : if (bPromoteTo8Bit)
1807 : {
1808 10 : poDS->bHas1BitAlpha = true;
1809 10 : CPLDebug(CODEC::debugId(),
1810 : "Alpha band is promoted from 1 bit to 8 bit");
1811 : }
1812 :
1813 1854 : auto poBand = new JP2OPJLikeRasterBand<CODEC, BASE>(
1814 : poDS, iBand, eDataType,
1815 922 : bPromoteTo8Bit ? 8 : localctx.psImage->comps[iBand - 1].prec,
1816 : bPromoteTo8Bit, nBlockXSize, nBlockYSize);
1817 932 : if (iBand == 1 && poCT != nullptr)
1818 24 : poBand->poCT = poCT;
1819 932 : poDS->SetBand(iBand, poBand);
1820 : }
1821 :
1822 : /* -------------------------------------------------------------------- */
1823 : /* Create overview datasets. */
1824 : /* -------------------------------------------------------------------- */
1825 760 : int nW = poDS->nRasterXSize;
1826 760 : int nH = poDS->nRasterYSize;
1827 760 : poDS->nParentXSize = poDS->nRasterXSize;
1828 760 : poDS->nParentYSize = poDS->nRasterYSize;
1829 :
1830 : /* Lower resolutions are not compatible with a color-table */
1831 760 : if (poCT != nullptr)
1832 24 : numResolutions = 0;
1833 :
1834 760 : if (poDS->bSingleTiled && poDS->bUseSetDecodeArea)
1835 : {
1836 580 : poDS->cacheNew(&localctx);
1837 : }
1838 760 : poDS->m_pnLastLevel = new int(-1);
1839 :
1840 : // Create overview datasets from JPEG2000 resolution levels.
1841 : // For Grok (canPerformDirectIO()=true), overviews lazily create their
1842 : // own codec on first read and use DirectRasterIO, so no block-size
1843 : // or decode-area constraints apply. For OpenJPEG, overviews require
1844 : // either bUseSetDecodeArea or even tile dimensions.
1845 1190 : while (poDS->nOverviewCount + 1 < numResolutions &&
1846 1117 : (nW > 128 || nH > 128) &&
1847 240 : (BASE::canPerformDirectIO() || poDS->bUseSetDecodeArea ||
1848 86 : ((nTileW % 2) == 0 && (nTileH % 2) == 0)))
1849 : {
1850 : // This must be this exact formula per the JPEG2000 standard
1851 117 : nW = (nW + 1) / 2;
1852 117 : nH = (nH + 1) / 2;
1853 :
1854 117 : poDS->papoOverviewDS = static_cast<JP2OPJLikeDataset<CODEC, BASE> **>(
1855 234 : CPLRealloc(poDS->papoOverviewDS,
1856 117 : (poDS->nOverviewCount + 1) *
1857 : sizeof(JP2OPJLikeDataset<CODEC, BASE> *)));
1858 117 : JP2OPJLikeDataset *poODS = new JP2OPJLikeDataset();
1859 117 : poODS->m_osFilename = poDS->m_osFilename;
1860 117 : poODS->nParentXSize = poDS->nRasterXSize;
1861 117 : poODS->nParentYSize = poDS->nRasterYSize;
1862 117 : poODS->SetDescription(poOpenInfo->pszFilename);
1863 117 : poODS->iLevel = poDS->nOverviewCount + 1;
1864 117 : poODS->bSingleTiled = poDS->bSingleTiled;
1865 117 : poODS->bUseSetDecodeArea = poDS->bUseSetDecodeArea;
1866 117 : poODS->nRedIndex = poDS->nRedIndex;
1867 117 : poODS->nGreenIndex = poDS->nGreenIndex;
1868 117 : poODS->nBlueIndex = poDS->nBlueIndex;
1869 117 : poODS->nAlphaIndex = poDS->nAlphaIndex;
1870 117 : if (BASE::canPerformDirectIO())
1871 : {
1872 : // DirectRasterIO bypasses block-based I/O, so block size
1873 : // is irrelevant; set to full overview dimensions.
1874 0 : nBlockXSize = nW;
1875 0 : nBlockYSize = nH;
1876 : }
1877 117 : else if (!poDS->bUseSetDecodeArea)
1878 : {
1879 83 : nTileW /= 2;
1880 83 : nTileH /= 2;
1881 83 : nBlockXSize = static_cast<int>(nTileW);
1882 83 : nBlockYSize = static_cast<int>(nTileH);
1883 : }
1884 : else
1885 : {
1886 34 : nBlockXSize = std::min(nW, static_cast<int>(nTileW));
1887 34 : nBlockYSize = std::min(nH, static_cast<int>(nTileH));
1888 : }
1889 :
1890 117 : poODS->eColorSpace = poDS->eColorSpace;
1891 117 : poODS->nRasterXSize = nW;
1892 117 : poODS->nRasterYSize = nH;
1893 117 : poODS->nBands = poDS->nBands;
1894 117 : poODS->fp_ = poDS->fp_;
1895 117 : poODS->nCodeStreamStart = nCodeStreamStart;
1896 117 : poODS->nCodeStreamLength = nCodeStreamLength;
1897 117 : poODS->bIs420 = bIs420;
1898 :
1899 117 : if (poODS->bSingleTiled && poODS->bUseSetDecodeArea)
1900 : {
1901 32 : poODS->cache(poDS);
1902 : }
1903 117 : poODS->m_pnLastLevel = poDS->m_pnLastLevel;
1904 117 : poODS->m_bStrict = poDS->m_bStrict;
1905 :
1906 117 : poODS->m_nX0 = poDS->m_nX0;
1907 117 : poODS->m_nY0 = poDS->m_nY0;
1908 :
1909 335 : for (iBand = 1; iBand <= poDS->nBands; iBand++)
1910 : {
1911 218 : const bool bPromoteTo8Bit =
1912 237 : iBand == poDS->nAlphaIndex + 1 &&
1913 19 : localctx.psImage
1914 19 : ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1)
1915 : ? 1
1916 : : 0]
1917 19 : .prec == 8 &&
1918 246 : localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
1919 9 : CPLFetchBool(
1920 9 : poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
1921 9 : CPLTestBool(CPLGetConfigOption(
1922 : "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
1923 :
1924 218 : poODS->SetBand(iBand,
1925 430 : new JP2OPJLikeRasterBand<CODEC, BASE>(
1926 : poODS, iBand, eDataType,
1927 : bPromoteTo8Bit
1928 : ? 8
1929 212 : : localctx.psImage->comps[iBand - 1].prec,
1930 : bPromoteTo8Bit, nBlockXSize, nBlockYSize));
1931 : }
1932 :
1933 117 : poDS->papoOverviewDS[poDS->nOverviewCount++] = poODS;
1934 : }
1935 :
1936 760 : poDS->openCompleteJP2(&localctx);
1937 :
1938 : /* -------------------------------------------------------------------- */
1939 : /* More metadata. */
1940 : /* -------------------------------------------------------------------- */
1941 760 : if (poDS->nBands > 1)
1942 : {
1943 73 : poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
1944 : "IMAGE_STRUCTURE");
1945 : }
1946 :
1947 760 : poOpenInfo->fpL = poDS->fp_;
1948 760 : vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
1949 760 : poDS->LoadJP2Metadata(poOpenInfo);
1950 760 : VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
1951 760 : poOpenInfo->fpL = nullptr;
1952 :
1953 760 : poDS->bHasGeoreferencingAtOpening =
1954 996 : (!poDS->m_oSRS.IsEmpty() || poDS->nGCPCount != 0 ||
1955 236 : poDS->bGeoTransformValid);
1956 :
1957 : /* -------------------------------------------------------------------- */
1958 : /* Vector layers */
1959 : /* -------------------------------------------------------------------- */
1960 760 : if (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
1961 : {
1962 66 : poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
1963 : "OPEN_REMOTE_GML", false));
1964 :
1965 : // If file opened in vector-only mode and there's no vector,
1966 : // return
1967 70 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
1968 4 : poDS->GetLayerCount() == 0)
1969 : {
1970 2 : delete poDS;
1971 2 : return nullptr;
1972 : }
1973 : }
1974 :
1975 : /* -------------------------------------------------------------------- */
1976 : /* Initialize any PAM information. */
1977 : /* -------------------------------------------------------------------- */
1978 758 : poDS->SetDescription(poOpenInfo->pszFilename);
1979 758 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
1980 :
1981 : /* -------------------------------------------------------------------- */
1982 : /* Check for overviews. */
1983 : /* -------------------------------------------------------------------- */
1984 758 : poDS->oOvManager.Initialize(poDS, poOpenInfo);
1985 :
1986 758 : return poDS;
1987 : }
1988 :
1989 : /************************************************************************/
1990 : /* WriteBox() */
1991 : /************************************************************************/
1992 :
1993 : template <typename CODEC, typename BASE>
1994 942 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteBox(VSILFILE *fp, GDALJP2Box *poBox)
1995 : {
1996 : GUInt32 nLBox;
1997 : GUInt32 nTBox;
1998 :
1999 942 : if (poBox == nullptr)
2000 0 : return true;
2001 :
2002 942 : nLBox = static_cast<int>(poBox->GetDataLength()) + 8;
2003 942 : nLBox = CPL_MSBWORD32(nLBox);
2004 :
2005 942 : memcpy(&nTBox, poBox->GetType(), 4);
2006 :
2007 942 : return VSIFWriteL(&nLBox, 4, 1, fp) == 1 &&
2008 1884 : VSIFWriteL(&nTBox, 4, 1, fp) == 1 &&
2009 942 : VSIFWriteL(poBox->GetWritableData(),
2010 1884 : static_cast<int>(poBox->GetDataLength()), 1, fp) == 1;
2011 : }
2012 :
2013 : /************************************************************************/
2014 : /* WriteGDALMetadataBox() */
2015 : /************************************************************************/
2016 :
2017 : template <typename CODEC, typename BASE>
2018 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteGDALMetadataBox(
2019 : VSILFILE *fp, GDALDataset *poSrcDS, CSLConstList papszOptions)
2020 : {
2021 19 : bool bRet = true;
2022 38 : GDALJP2Box *poBox = GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
2023 19 : poSrcDS, CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false));
2024 19 : if (poBox)
2025 6 : bRet = WriteBox(fp, poBox);
2026 19 : delete poBox;
2027 19 : return bRet;
2028 : }
2029 :
2030 : /************************************************************************/
2031 : /* WriteXMLBoxes() */
2032 : /************************************************************************/
2033 :
2034 : template <typename CODEC, typename BASE>
2035 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMLBoxes(VSILFILE *fp,
2036 : GDALDataset *poSrcDS)
2037 : {
2038 19 : bool bRet = true;
2039 19 : int nBoxes = 0;
2040 19 : GDALJP2Box **papoBoxes = GDALJP2Metadata::CreateXMLBoxes(poSrcDS, &nBoxes);
2041 21 : for (int i = 0; i < nBoxes; i++)
2042 : {
2043 2 : if (!WriteBox(fp, papoBoxes[i]))
2044 0 : bRet = false;
2045 2 : delete papoBoxes[i];
2046 : }
2047 19 : CPLFree(papoBoxes);
2048 19 : return bRet;
2049 : }
2050 :
2051 : /************************************************************************/
2052 : /* WriteXMPBox() */
2053 : /************************************************************************/
2054 :
2055 : template <typename CODEC, typename BASE>
2056 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMPBox(VSILFILE *fp,
2057 : GDALDataset *poSrcDS)
2058 : {
2059 19 : bool bRet = true;
2060 19 : GDALJP2Box *poBox = GDALJP2Metadata::CreateXMPBox(poSrcDS);
2061 19 : if (poBox)
2062 2 : bRet = WriteBox(fp, poBox);
2063 19 : delete poBox;
2064 19 : return bRet;
2065 : }
2066 :
2067 : /************************************************************************/
2068 : /* WriteIPRBox() */
2069 : /************************************************************************/
2070 :
2071 : template <typename CODEC, typename BASE>
2072 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteIPRBox(VSILFILE *fp,
2073 : GDALDataset *poSrcDS)
2074 : {
2075 19 : bool bRet = true;
2076 19 : GDALJP2Box *poBox = GDALJP2Metadata::CreateIPRBox(poSrcDS);
2077 19 : if (poBox)
2078 2 : bRet = WriteBox(fp, poBox);
2079 19 : delete poBox;
2080 19 : return bRet;
2081 : }
2082 :
2083 : /************************************************************************/
2084 : /* FloorPowerOfTwo() */
2085 : /************************************************************************/
2086 :
2087 544 : static int FloorPowerOfTwo(int nVal)
2088 : {
2089 544 : int nBits = 0;
2090 3805 : while (nVal > 1)
2091 : {
2092 3261 : nBits++;
2093 3261 : nVal >>= 1;
2094 : }
2095 544 : return 1 << nBits;
2096 : }
2097 :
2098 : /************************************************************************/
2099 : /* CreateCopy() */
2100 : /************************************************************************/
2101 :
2102 : template <typename CODEC, typename BASE>
2103 282 : GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::CreateCopy(
2104 : const char *pszFilename, GDALDataset *poSrcDS, CPL_UNUSED int bStrict,
2105 : CSLConstList papszOptions, GDALProgressFunc pfnProgress,
2106 : void *pProgressData)
2107 :
2108 : {
2109 282 : int nBands = poSrcDS->GetRasterCount();
2110 282 : int nXSize = poSrcDS->GetRasterXSize();
2111 282 : int nYSize = poSrcDS->GetRasterYSize();
2112 :
2113 282 : if (nBands == 0 || nBands > 16384)
2114 : {
2115 2 : CPLError(
2116 : CE_Failure, CPLE_NotSupported,
2117 : "Unable to export files with %d bands. Must be >= 1 and <= 16384",
2118 : nBands);
2119 2 : return nullptr;
2120 : }
2121 :
2122 280 : GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
2123 280 : if (poCT != nullptr && nBands != 1)
2124 : {
2125 1 : CPLError(CE_Failure, CPLE_NotSupported,
2126 : "JP2 driver only supports a color table for a "
2127 : "single-band dataset");
2128 1 : return nullptr;
2129 : }
2130 :
2131 279 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
2132 279 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
2133 279 : if (eDataType != GDT_UInt8 && eDataType != GDT_Int16 &&
2134 8 : eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
2135 : eDataType != GDT_UInt32)
2136 : {
2137 6 : CPLError(CE_Failure, CPLE_NotSupported,
2138 : "JP2 driver only supports creating Byte, GDT_Int16, "
2139 : "GDT_UInt16, GDT_Int32, GDT_UInt32");
2140 6 : return nullptr;
2141 : }
2142 :
2143 : /* -------------------------------------------------------------------- */
2144 : /* Transcode short-circuit (Grok only). */
2145 : /* -------------------------------------------------------------------- */
2146 273 : if (CPLFetchBool(papszOptions, "TRANSCODE", false))
2147 : {
2148 0 : if (!BASE::canPerformDirectIO())
2149 : {
2150 0 : CPLError(CE_Failure, CPLE_NotSupported,
2151 : "TRANSCODE=YES is only supported by the JP2Grok driver");
2152 0 : return nullptr;
2153 : }
2154 :
2155 0 : CPLString osSrcFilename(poSrcDS->GetDescription());
2156 0 : if (poSrcDS->GetDriver() != nullptr &&
2157 0 : poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
2158 : {
2159 0 : VRTDataset *poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
2160 0 : if (poVRTDS)
2161 : {
2162 : GDALDataset *poSimpleSourceDS =
2163 0 : poVRTDS->GetSingleSimpleSource();
2164 0 : if (poSimpleSourceDS)
2165 0 : osSrcFilename = poSimpleSourceDS->GetDescription();
2166 : }
2167 : }
2168 :
2169 0 : if (!CODEC::transcode(osSrcFilename, pszFilename, poSrcDS,
2170 : papszOptions))
2171 0 : return nullptr;
2172 :
2173 0 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
2174 0 : return Open(&oOpenInfo);
2175 : }
2176 :
2177 273 : const bool bInspireTG = CPLFetchBool(papszOptions, "INSPIRE_TG", false);
2178 :
2179 : /* -------------------------------------------------------------------- */
2180 : /* Analyze creation options. */
2181 : /* -------------------------------------------------------------------- */
2182 273 : auto eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
2183 273 : const char *pszCodec = CSLFetchNameValueDef(papszOptions, "CODEC", nullptr);
2184 273 : if (pszCodec)
2185 : {
2186 14 : if (EQUAL(pszCodec, "JP2"))
2187 5 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
2188 9 : else if (EQUAL(pszCodec, "J2K"))
2189 9 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
2190 : else
2191 : {
2192 0 : CPLError(CE_Warning, CPLE_NotSupported,
2193 : "Unsupported value for CODEC : %s. Defaulting to J2K",
2194 : pszCodec);
2195 : }
2196 : }
2197 : else
2198 : {
2199 259 : if (strlen(pszFilename) > 4 &&
2200 259 : EQUAL(pszFilename + strlen(pszFilename) - 4, ".JP2"))
2201 : {
2202 224 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
2203 : }
2204 : }
2205 273 : if (eCodecFormat != CODEC::cvtenum(JP2_CODEC_JP2) && bInspireTG)
2206 : {
2207 1 : CPLError(CE_Warning, CPLE_NotSupported,
2208 : "INSPIRE_TG=YES mandates CODEC=JP2 (TG requirement 21)");
2209 1 : return nullptr;
2210 : }
2211 :
2212 : // NOTE: if changing the default block size, the logic in nitfdataset.cpp
2213 : // CreateCopy() will have to be changed as well.
2214 272 : int nBlockXSize =
2215 272 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "1024"));
2216 272 : int nBlockYSize =
2217 272 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "1024"));
2218 272 : if (nBlockXSize <= 0 || nBlockYSize <= 0)
2219 : {
2220 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
2221 0 : return nullptr;
2222 : }
2223 :
2224 : // By default do not generate tile sizes larger than the dataset
2225 : // dimensions
2226 544 : if (!CPLFetchBool(papszOptions, "BLOCKSIZE_STRICT", false) &&
2227 272 : !CPLFetchBool(papszOptions, "@BLOCKSIZE_STRICT", false))
2228 : {
2229 268 : if (nBlockXSize < 32 || nBlockYSize < 32)
2230 : {
2231 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
2232 0 : return nullptr;
2233 : }
2234 :
2235 268 : if (nXSize < nBlockXSize)
2236 : {
2237 243 : CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
2238 : nBlockXSize, nXSize);
2239 243 : nBlockXSize = nXSize;
2240 : }
2241 268 : if (nYSize < nBlockYSize)
2242 : {
2243 244 : CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
2244 : nBlockYSize, nYSize);
2245 244 : nBlockYSize = nYSize;
2246 : }
2247 : }
2248 :
2249 272 : JP2_PROG_ORDER eProgOrder = JP2_LRCP;
2250 : const char *pszPROGORDER =
2251 272 : CSLFetchNameValueDef(papszOptions, "PROGRESSION", "LRCP");
2252 272 : if (EQUAL(pszPROGORDER, "LRCP"))
2253 272 : eProgOrder = JP2_LRCP;
2254 0 : else if (EQUAL(pszPROGORDER, "RLCP"))
2255 0 : eProgOrder = JP2_RLCP;
2256 0 : else if (EQUAL(pszPROGORDER, "RPCL"))
2257 0 : eProgOrder = JP2_RPCL;
2258 0 : else if (EQUAL(pszPROGORDER, "PCRL"))
2259 0 : eProgOrder = JP2_PCRL;
2260 0 : else if (EQUAL(pszPROGORDER, "CPRL"))
2261 0 : eProgOrder = JP2_CPRL;
2262 : else
2263 : {
2264 0 : CPLError(CE_Warning, CPLE_NotSupported,
2265 : "Unsupported value for PROGRESSION : %s. Defaulting to LRCP",
2266 : pszPROGORDER);
2267 : }
2268 :
2269 272 : const bool bIsIrreversible =
2270 272 : !CPLFetchBool(papszOptions, "REVERSIBLE", poCT != nullptr);
2271 :
2272 544 : std::vector<double> adfRates;
2273 : const char *pszQuality =
2274 272 : CSLFetchNameValueDef(papszOptions, "QUALITY", nullptr);
2275 272 : double dfDefaultQuality = (poCT != nullptr) ? 100.0 : 25.0;
2276 272 : if (pszQuality)
2277 : {
2278 : char **papszTokens =
2279 41 : CSLTokenizeStringComplex(pszQuality, ",", FALSE, FALSE);
2280 158 : for (int i = 0; papszTokens[i] != nullptr; i++)
2281 : {
2282 117 : double dfQuality = CPLAtof(papszTokens[i]);
2283 117 : if (dfQuality > 0 && dfQuality <= 100)
2284 : {
2285 117 : double dfRate = 100 / dfQuality;
2286 117 : adfRates.push_back(dfRate);
2287 : }
2288 : else
2289 : {
2290 0 : CPLError(CE_Warning, CPLE_NotSupported,
2291 : "Unsupported value for QUALITY: %s. Defaulting to "
2292 : "single-layer, with quality=%.0f",
2293 0 : papszTokens[i], dfDefaultQuality);
2294 0 : adfRates.resize(0);
2295 0 : break;
2296 : }
2297 : }
2298 41 : if (papszTokens[0] == nullptr)
2299 : {
2300 0 : CPLError(CE_Warning, CPLE_NotSupported,
2301 : "Unsupported value for QUALITY: %s. Defaulting to "
2302 : "single-layer, with quality=%.0f",
2303 : pszQuality, dfDefaultQuality);
2304 : }
2305 41 : CSLDestroy(papszTokens);
2306 : }
2307 272 : if (adfRates.empty())
2308 : {
2309 231 : adfRates.push_back(100. / dfDefaultQuality);
2310 231 : assert(!adfRates.empty());
2311 : }
2312 :
2313 272 : if (poCT != nullptr && (bIsIrreversible || adfRates.back() != 1.0))
2314 : {
2315 2 : CPLError(CE_Warning, CPLE_AppDefined,
2316 : "Encoding a dataset with a color table with REVERSIBLE != YES "
2317 : "or QUALITY != 100 will likely lead to bad visual results");
2318 : }
2319 :
2320 272 : const int nMaxTileDim = std::max(nBlockXSize, nBlockYSize);
2321 272 : const int nMinTileDim = std::min(nBlockXSize, nBlockYSize);
2322 272 : int nNumResolutions = 1;
2323 : /* Pickup a reasonable value compatible with PROFILE_1 requirements */
2324 363 : while ((nMaxTileDim >> (nNumResolutions - 1)) > 128 &&
2325 92 : (nMinTileDim >> nNumResolutions) > 0)
2326 91 : nNumResolutions++;
2327 272 : int nMinProfile1Resolutions = nNumResolutions;
2328 : const char *pszResolutions =
2329 272 : CSLFetchNameValueDef(papszOptions, "RESOLUTIONS", nullptr);
2330 272 : if (pszResolutions)
2331 : {
2332 10 : nNumResolutions = atoi(pszResolutions);
2333 10 : if (nNumResolutions <= 0 || nNumResolutions >= 32 ||
2334 9 : (nMinTileDim >> nNumResolutions) == 0 ||
2335 9 : (nMaxTileDim >> nNumResolutions) == 0)
2336 : {
2337 1 : CPLError(CE_Warning, CPLE_NotSupported,
2338 : "Unsupported value for RESOLUTIONS : %s. Defaulting to %d",
2339 : pszResolutions, nMinProfile1Resolutions);
2340 1 : nNumResolutions = nMinProfile1Resolutions;
2341 : }
2342 : }
2343 272 : int nRedBandIndex = -1;
2344 272 : int nGreenBandIndex = -1;
2345 272 : int nBlueBandIndex = -1;
2346 272 : int nAlphaBandIndex = -1;
2347 611 : for (int i = 0; i < nBands; i++)
2348 : {
2349 : GDALColorInterp eInterp =
2350 339 : poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation();
2351 339 : if (eInterp == GCI_RedBand)
2352 15 : nRedBandIndex = i;
2353 324 : else if (eInterp == GCI_GreenBand)
2354 15 : nGreenBandIndex = i;
2355 309 : else if (eInterp == GCI_BlueBand)
2356 15 : nBlueBandIndex = i;
2357 294 : else if (eInterp == GCI_AlphaBand)
2358 7 : nAlphaBandIndex = i;
2359 : }
2360 272 : const char *pszAlpha = CSLFetchNameValue(papszOptions, "ALPHA");
2361 275 : if (nAlphaBandIndex < 0 && nBands > 1 && pszAlpha != nullptr &&
2362 3 : CPLTestBool(pszAlpha))
2363 : {
2364 3 : nAlphaBandIndex = nBands - 1;
2365 : }
2366 :
2367 272 : const char *pszYCBCR420 = CSLFetchNameValue(papszOptions, "YCBCR420");
2368 272 : int bYCBCR420 = FALSE;
2369 272 : if (pszYCBCR420 && CPLTestBool(pszYCBCR420))
2370 : {
2371 2 : if ((nBands == 3 || nBands == 4) && eDataType == GDT_UInt8 &&
2372 2 : nRedBandIndex == 0 && nGreenBandIndex == 1 && nBlueBandIndex == 2)
2373 : {
2374 2 : if (((nXSize % 2) == 0 && (nYSize % 2) == 0 &&
2375 2 : (nBlockXSize % 2) == 0 && (nBlockYSize % 2) == 0))
2376 : {
2377 2 : bYCBCR420 = TRUE;
2378 : }
2379 : else
2380 : {
2381 0 : CPLError(CE_Warning, CPLE_NotSupported,
2382 : "YCBCR420 unsupported when image size and/or tile "
2383 : "size are not multiple of 2");
2384 : }
2385 : }
2386 : else
2387 : {
2388 0 : CPLError(CE_Warning, CPLE_NotSupported,
2389 : "YCBCR420 unsupported with this image band count and/or "
2390 : "data byte");
2391 : }
2392 : }
2393 :
2394 272 : const char *pszYCC = CSLFetchNameValue(papszOptions, "YCC");
2395 294 : int bYCC = ((nBands == 3 || nBands == 4) &&
2396 22 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "YCC", "TRUE")));
2397 :
2398 272 : if (bYCBCR420 && bYCC)
2399 : {
2400 2 : if (pszYCC != nullptr)
2401 : {
2402 0 : CPLError(CE_Warning, CPLE_NotSupported,
2403 : "YCC unsupported when YCbCr requesting");
2404 : }
2405 2 : bYCC = FALSE;
2406 : }
2407 :
2408 : /* -------------------------------------------------------------------- */
2409 : /* Deal with codeblocks size */
2410 : /* -------------------------------------------------------------------- */
2411 :
2412 : int nCblockW =
2413 272 : atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_WIDTH", "64"));
2414 : int nCblockH =
2415 272 : atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_HEIGHT", "64"));
2416 272 : if (nCblockW < 4 || nCblockW > 1024 || nCblockH < 4 || nCblockH > 1024)
2417 : {
2418 4 : CPLError(CE_Warning, CPLE_NotSupported,
2419 : "Invalid values for codeblock size. Defaulting to 64x64");
2420 4 : nCblockW = 64;
2421 4 : nCblockH = 64;
2422 : }
2423 268 : else if (nCblockW * nCblockH > 4096)
2424 : {
2425 1 : CPLError(CE_Warning, CPLE_NotSupported,
2426 : "Invalid values for codeblock size. "
2427 : "CODEBLOCK_WIDTH * CODEBLOCK_HEIGHT should be <= 4096. "
2428 : "Defaulting to 64x64");
2429 1 : nCblockW = 64;
2430 1 : nCblockH = 64;
2431 : }
2432 272 : int nCblockW_po2 = FloorPowerOfTwo(nCblockW);
2433 272 : int nCblockH_po2 = FloorPowerOfTwo(nCblockH);
2434 272 : if (nCblockW_po2 != nCblockW || nCblockH_po2 != nCblockH)
2435 : {
2436 1 : CPLError(CE_Warning, CPLE_NotSupported,
2437 : "Non power of two values used for codeblock size. "
2438 : "Using to %dx%d",
2439 : nCblockW_po2, nCblockH_po2);
2440 : }
2441 272 : nCblockW = nCblockW_po2;
2442 272 : nCblockH = nCblockH_po2;
2443 :
2444 : /* -------------------------------------------------------------------- */
2445 : /* Deal with codestream PROFILE */
2446 : /* -------------------------------------------------------------------- */
2447 : const char *pszProfile =
2448 272 : CSLFetchNameValueDef(papszOptions, "PROFILE", "AUTO");
2449 272 : int bProfile1 = FALSE;
2450 272 : if (EQUAL(pszProfile, "UNRESTRICTED"))
2451 : {
2452 1 : bProfile1 = FALSE;
2453 1 : if (bInspireTG)
2454 : {
2455 1 : CPLError(CE_Failure, CPLE_NotSupported,
2456 : "INSPIRE_TG=YES mandates PROFILE=PROFILE_1 (TG "
2457 : "requirement 21)");
2458 1 : return nullptr;
2459 : }
2460 : }
2461 271 : else if (EQUAL(pszProfile, "UNRESTRICTED_FORCED"))
2462 : {
2463 0 : bProfile1 = FALSE;
2464 : }
2465 271 : else if (EQUAL(pszProfile,
2466 : "PROFILE_1_FORCED")) /* For debug only: can produce
2467 : inconsistent codestream */
2468 : {
2469 0 : bProfile1 = TRUE;
2470 : }
2471 : else
2472 : {
2473 271 : if (!(EQUAL(pszProfile, "PROFILE_1") || EQUAL(pszProfile, "AUTO")))
2474 : {
2475 0 : CPLError(CE_Warning, CPLE_NotSupported,
2476 : "Unsupported value for PROFILE : %s. Defaulting to AUTO",
2477 : pszProfile);
2478 0 : pszProfile = "AUTO";
2479 : }
2480 :
2481 271 : bProfile1 = TRUE;
2482 271 : const char *pszReq21OrEmpty = bInspireTG ? " (TG requirement 21)" : "";
2483 271 : if ((nBlockXSize != nXSize || nBlockYSize != nYSize) &&
2484 24 : (nBlockXSize != nBlockYSize || nBlockXSize > 1024 ||
2485 19 : nBlockYSize > 1024))
2486 : {
2487 5 : bProfile1 = FALSE;
2488 5 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2489 : {
2490 2 : CPLError(
2491 : CE_Failure, CPLE_NotSupported,
2492 : "Tile dimensions incompatible with PROFILE_1%s. "
2493 : "Should be whole image or square with dimension <= 1024.",
2494 : pszReq21OrEmpty);
2495 2 : return nullptr;
2496 : }
2497 : }
2498 269 : if ((nMaxTileDim >> (nNumResolutions - 1)) > 128)
2499 : {
2500 4 : bProfile1 = FALSE;
2501 4 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2502 : {
2503 1 : CPLError(CE_Failure, CPLE_NotSupported,
2504 : "Number of resolutions incompatible with PROFILE_1%s. "
2505 : "Should be at least %d.",
2506 : pszReq21OrEmpty, nMinProfile1Resolutions);
2507 1 : return nullptr;
2508 : }
2509 : }
2510 268 : if (nCblockW > 64 || nCblockH > 64)
2511 : {
2512 2 : bProfile1 = FALSE;
2513 2 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2514 : {
2515 2 : CPLError(CE_Failure, CPLE_NotSupported,
2516 : "Codeblock width incompatible with PROFILE_1%s. "
2517 : "Codeblock width or height should be <= 64.",
2518 : pszReq21OrEmpty);
2519 2 : return nullptr;
2520 : }
2521 : }
2522 : }
2523 :
2524 : /* -------------------------------------------------------------------- */
2525 : /* Work out the precision. */
2526 : /* -------------------------------------------------------------------- */
2527 : int nBits;
2528 266 : const int nDTBits = GDALGetDataTypeSizeBits(eDataType);
2529 :
2530 266 : if (CSLFetchNameValue(papszOptions, "NBITS") != nullptr)
2531 : {
2532 22 : nBits = atoi(CSLFetchNameValue(papszOptions, "NBITS"));
2533 22 : if (bInspireTG &&
2534 1 : !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32))
2535 : {
2536 1 : CPLError(CE_Failure, CPLE_NotSupported,
2537 : "INSPIRE_TG=YES mandates NBITS=1,8,16 or 32 (TG "
2538 : "requirement 24)");
2539 1 : return nullptr;
2540 : }
2541 : }
2542 244 : else if (poSrcDS->GetRasterBand(1)->GetMetadataItem(
2543 244 : "NBITS", "IMAGE_STRUCTURE") != nullptr)
2544 : {
2545 3 : nBits = atoi(poSrcDS->GetRasterBand(1)->GetMetadataItem(
2546 : "NBITS", "IMAGE_STRUCTURE"));
2547 3 : if (bInspireTG &&
2548 1 : !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32))
2549 : {
2550 : /* Implements "NOTE If the original data do not satisfy this "
2551 : "requirement, they will be converted in a representation using "
2552 : "the next higher power of 2" */
2553 1 : nBits = nDTBits;
2554 : }
2555 : }
2556 : else
2557 : {
2558 241 : nBits = nDTBits;
2559 : }
2560 :
2561 265 : if ((nDTBits == 8 && nBits > 8) ||
2562 265 : (nDTBits == 16 && (nBits <= 8 || nBits > 16)) ||
2563 2 : (nDTBits == 32 && (nBits <= 16 || nBits > 32)))
2564 : {
2565 0 : CPLError(CE_Warning, CPLE_NotSupported,
2566 : "Inconsistent NBITS value with data type. Using %d", nDTBits);
2567 : }
2568 :
2569 : /* -------------------------------------------------------------------- */
2570 : /* Georeferencing options */
2571 : /* -------------------------------------------------------------------- */
2572 :
2573 265 : bool bGMLJP2Option = CPLFetchBool(papszOptions, "GMLJP2", true);
2574 265 : int nGMLJP2Version = 1;
2575 : const char *pszGMLJP2V2Def =
2576 265 : CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
2577 265 : if (pszGMLJP2V2Def != nullptr)
2578 : {
2579 28 : bGMLJP2Option = true;
2580 28 : nGMLJP2Version = 2;
2581 28 : if (bInspireTG)
2582 : {
2583 0 : CPLError(CE_Warning, CPLE_NotSupported,
2584 : "INSPIRE_TG=YES is only compatible with GMLJP2 v1");
2585 0 : return nullptr;
2586 : }
2587 : }
2588 265 : const bool bGeoJP2Option = CPLFetchBool(papszOptions, "GeoJP2", true);
2589 :
2590 530 : GDALJP2Metadata oJP2MD;
2591 :
2592 265 : int bGeoreferencingCompatOfGeoJP2 = FALSE;
2593 265 : int bGeoreferencingCompatOfGMLJP2 = FALSE;
2594 271 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
2595 6 : (bGMLJP2Option || bGeoJP2Option))
2596 : {
2597 220 : if (poSrcDS->GetGCPCount() > 0)
2598 : {
2599 3 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2600 3 : oJP2MD.SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs());
2601 3 : oJP2MD.SetSpatialRef(poSrcDS->GetGCPSpatialRef());
2602 : }
2603 : else
2604 : {
2605 217 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
2606 217 : if (poSRS)
2607 : {
2608 57 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2609 57 : oJP2MD.SetSpatialRef(poSRS);
2610 : }
2611 217 : GDALGeoTransform gt;
2612 217 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
2613 : {
2614 164 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2615 164 : oJP2MD.SetGeoTransform(gt);
2616 164 : if (poSRS && !poSRS->IsEmpty())
2617 : {
2618 57 : bGeoreferencingCompatOfGMLJP2 =
2619 57 : GDALJP2Metadata::IsSRSCompatible(poSRS);
2620 57 : if (!bGeoreferencingCompatOfGMLJP2)
2621 : {
2622 1 : CPLDebug(
2623 : CODEC::debugId(),
2624 : "Cannot write GMLJP2 box due to unsupported SRS");
2625 : }
2626 : }
2627 : }
2628 : }
2629 220 : if (poSrcDS->GetMetadata("RPC") != nullptr)
2630 : {
2631 1 : oJP2MD.SetRPCMD(poSrcDS->GetMetadata("RPC"));
2632 1 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2633 : }
2634 :
2635 : const char *pszAreaOrPoint =
2636 220 : poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
2637 261 : oJP2MD.bPixelIsPoint = pszAreaOrPoint != nullptr &&
2638 41 : EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
2639 :
2640 436 : if (bGMLJP2Option &&
2641 216 : CPLGetConfigOption("GMLJP2OVERRIDE", nullptr) != nullptr)
2642 : {
2643 : // Force V1 since this is the branch in which the hack is
2644 : // implemented
2645 7 : nGMLJP2Version = 1;
2646 7 : bGeoreferencingCompatOfGMLJP2 = TRUE;
2647 : }
2648 : }
2649 :
2650 265 : if (CSLFetchNameValue(papszOptions, "GMLJP2") != nullptr && bGMLJP2Option &&
2651 : !bGeoreferencingCompatOfGMLJP2)
2652 : {
2653 0 : CPLError(CE_Warning, CPLE_AppDefined,
2654 : "GMLJP2 box was explicitly required but cannot be written due "
2655 : "to lack of georeferencing and/or unsupported georeferencing "
2656 : "for GMLJP2");
2657 : }
2658 :
2659 265 : if (CSLFetchNameValue(papszOptions, "GeoJP2") != nullptr && bGeoJP2Option &&
2660 : !bGeoreferencingCompatOfGeoJP2)
2661 : {
2662 0 : CPLError(CE_Warning, CPLE_AppDefined,
2663 : "GeoJP2 box was explicitly required but cannot be written due "
2664 : "to lack of georeferencing");
2665 : }
2666 : const bool bGeoBoxesAfter =
2667 265 : CPLFetchBool(papszOptions, "GEOBOXES_AFTER_JP2C", bInspireTG);
2668 265 : GDALJP2Box *poGMLJP2Box = nullptr;
2669 265 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) && bGMLJP2Option &&
2670 : bGeoreferencingCompatOfGMLJP2)
2671 : {
2672 61 : if (nGMLJP2Version == 1)
2673 33 : poGMLJP2Box = oJP2MD.CreateGMLJP2(nXSize, nYSize);
2674 : else
2675 : poGMLJP2Box =
2676 28 : oJP2MD.CreateGMLJP2V2(nXSize, nYSize, pszGMLJP2V2Def, poSrcDS);
2677 61 : if (poGMLJP2Box == nullptr)
2678 3 : return nullptr;
2679 : }
2680 :
2681 : /* ---------------------------------------------------------------- */
2682 : /* If the input driver is identified as "GEORASTER" the following */
2683 : /* section will try to dump a ORACLE GeoRaster JP2 BLOB into a file */
2684 : /* ---------------------------------------------------------------- */
2685 :
2686 262 : if (EQUAL(poSrcDS->GetDriverName(), "GEORASTER"))
2687 : {
2688 : const char *pszGEOR_compress =
2689 0 : poSrcDS->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
2690 :
2691 0 : if (pszGEOR_compress == nullptr)
2692 : {
2693 0 : pszGEOR_compress = "NONE";
2694 : }
2695 :
2696 : /* Check if the JP2 BLOB needs re-shaping */
2697 :
2698 0 : bool bGEOR_reshape = false;
2699 :
2700 0 : const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
2701 : "BLOCKYSIZE",
2702 : "QUALITY",
2703 : "REVERSIBLE",
2704 : "RESOLUTIONS",
2705 : "PROGRESSION",
2706 : "SOP",
2707 : "EPH",
2708 : "YCBCR420",
2709 : "YCC",
2710 : "NBITS",
2711 : "1BIT_ALPHA",
2712 : "PRECINCTS",
2713 : "TILEPARTS",
2714 : "CODEBLOCK_WIDTH",
2715 : "CODEBLOCK_HEIGHT",
2716 : "PLT",
2717 : "TLM",
2718 : nullptr};
2719 :
2720 0 : for (int i = 0; apszIgnoredOptions[i]; i++)
2721 : {
2722 0 : if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
2723 : {
2724 0 : bGEOR_reshape = true;
2725 : }
2726 : }
2727 :
2728 0 : if (CSLFetchNameValue(papszOptions, "USE_SRC_CODESTREAM"))
2729 : {
2730 0 : bGEOR_reshape = false;
2731 : }
2732 :
2733 0 : char **papszGEOR_files = poSrcDS->GetFileList();
2734 :
2735 0 : if (EQUAL(pszGEOR_compress, "JP2-F") && CSLCount(papszGEOR_files) > 0 &&
2736 0 : bGEOR_reshape == false)
2737 : {
2738 :
2739 0 : const char *pszVsiOciLob = papszGEOR_files[0];
2740 :
2741 0 : VSILFILE *fpBlob = VSIFOpenL(pszVsiOciLob, "r");
2742 0 : if (fpBlob == nullptr)
2743 : {
2744 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
2745 : pszVsiOciLob);
2746 0 : delete poGMLJP2Box;
2747 0 : return nullptr;
2748 : }
2749 0 : VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
2750 0 : if (fp == nullptr)
2751 : {
2752 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
2753 : pszFilename);
2754 0 : delete poGMLJP2Box;
2755 0 : VSIFCloseL(fpBlob);
2756 0 : return nullptr;
2757 : }
2758 :
2759 0 : VSIFSeekL(fpBlob, 0, SEEK_END);
2760 :
2761 0 : size_t nBlobSize = static_cast<size_t>(VSIFTellL(fpBlob));
2762 0 : size_t nChunk = GDALGetCacheMax() / 4;
2763 0 : size_t nSize = 0;
2764 0 : size_t nCount = 0;
2765 :
2766 0 : void *pBuffer = VSI_MALLOC_VERBOSE(nChunk);
2767 0 : if (pBuffer == nullptr)
2768 : {
2769 0 : delete poGMLJP2Box;
2770 0 : VSIFCloseL(fpBlob);
2771 0 : VSIFCloseL(fp);
2772 0 : return nullptr;
2773 : }
2774 :
2775 0 : VSIFSeekL(fpBlob, 0, SEEK_SET);
2776 :
2777 0 : while ((nSize = VSIFReadL(pBuffer, 1, nChunk, fpBlob)) > 0)
2778 : {
2779 0 : VSIFWriteL(pBuffer, 1, nSize, fp);
2780 0 : nCount += nSize;
2781 0 : pfnProgress(static_cast<double>(nCount) /
2782 : static_cast<double>(nBlobSize),
2783 : nullptr, pProgressData);
2784 : }
2785 :
2786 0 : CPLFree(pBuffer);
2787 0 : VSIFCloseL(fpBlob);
2788 :
2789 0 : VSIFCloseL(fp);
2790 :
2791 : /* Return the GDALDaset object */
2792 :
2793 0 : GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
2794 0 : GDALDataset *poDS = JP2OPJLikeDataset::Open(&oOpenInfo);
2795 :
2796 : /* Copy essential metadata */
2797 :
2798 0 : GDALGeoTransform gt;
2799 0 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
2800 : {
2801 0 : poDS->SetGeoTransform(gt);
2802 : }
2803 :
2804 0 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
2805 0 : if (poSRS)
2806 : {
2807 0 : poDS->SetSpatialRef(poSRS);
2808 : }
2809 :
2810 0 : delete poGMLJP2Box;
2811 0 : return poDS;
2812 : }
2813 : }
2814 :
2815 : /* -------------------------------------------------------------------- */
2816 : /* Setup encoder */
2817 : /* -------------------------------------------------------------------- */
2818 :
2819 524 : JP2OPJLikeDataset oTmpDS;
2820 262 : int numThreads = oTmpDS.GetNumThreads();
2821 :
2822 262 : CODEC localctx;
2823 262 : localctx.allocComponentParams(nBands);
2824 : int iBand;
2825 262 : int bSamePrecision = TRUE;
2826 262 : int b1BitAlpha = FALSE;
2827 591 : for (iBand = 0; iBand < nBands; iBand++)
2828 : {
2829 329 : localctx.pasBandParams[iBand].x0 = 0;
2830 329 : localctx.pasBandParams[iBand].y0 = 0;
2831 329 : if (bYCBCR420 && (iBand == 1 || iBand == 2))
2832 : {
2833 4 : localctx.pasBandParams[iBand].dx = 2;
2834 4 : localctx.pasBandParams[iBand].dy = 2;
2835 4 : localctx.pasBandParams[iBand].w = nXSize / 2;
2836 4 : localctx.pasBandParams[iBand].h = nYSize / 2;
2837 : }
2838 : else
2839 : {
2840 325 : localctx.pasBandParams[iBand].dx = 1;
2841 325 : localctx.pasBandParams[iBand].dy = 1;
2842 325 : localctx.pasBandParams[iBand].w = nXSize;
2843 325 : localctx.pasBandParams[iBand].h = nYSize;
2844 : }
2845 :
2846 329 : localctx.pasBandParams[iBand].sgnd =
2847 329 : (eDataType == GDT_Int16 || eDataType == GDT_Int32);
2848 329 : localctx.pasBandParams[iBand].prec = nBits;
2849 :
2850 : const char *pszNBits =
2851 329 : poSrcDS->GetRasterBand(iBand + 1)->GetMetadataItem(
2852 : "NBITS", "IMAGE_STRUCTURE");
2853 : /* Recommendation 38 In the case of an opacity channel, the bit depth
2854 : * should be 1-bit. */
2855 339 : if (iBand == nAlphaBandIndex &&
2856 0 : ((pszNBits != nullptr && EQUAL(pszNBits, "1")) ||
2857 10 : CPLFetchBool(papszOptions, "1BIT_ALPHA", bInspireTG)))
2858 : {
2859 3 : if (iBand != nBands - 1 && nBits != 1)
2860 : {
2861 : /* Might be a bug in openjpeg, but it seems that if the alpha */
2862 : /* band is the first one, it would select 1-bit for all
2863 : * channels... */
2864 0 : CPLError(CE_Warning, CPLE_NotSupported,
2865 : "Cannot output 1-bit alpha channel if it is not the "
2866 : "last one");
2867 : }
2868 : else
2869 : {
2870 3 : CPLDebug(CODEC::debugId(), "Using 1-bit alpha channel");
2871 3 : localctx.pasBandParams[iBand].sgnd = 0;
2872 3 : localctx.pasBandParams[iBand].prec = 1;
2873 3 : bSamePrecision = FALSE;
2874 3 : b1BitAlpha = TRUE;
2875 : }
2876 : }
2877 : }
2878 :
2879 262 : if (bInspireTG && nAlphaBandIndex >= 0 && !b1BitAlpha)
2880 : {
2881 0 : CPLError(
2882 : CE_Warning, CPLE_NotSupported,
2883 : "INSPIRE_TG=YES recommends 1BIT_ALPHA=YES (Recommendation 38)");
2884 : }
2885 262 : auto eColorSpace = CODEC::cvtenum(JP2_CLRSPC_GRAY);
2886 :
2887 262 : if (bYCBCR420)
2888 : {
2889 2 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SYCC);
2890 : }
2891 260 : else if ((nBands == 3 || nBands == 4) && nRedBandIndex >= 0 &&
2892 13 : nGreenBandIndex >= 0 && nBlueBandIndex >= 0)
2893 : {
2894 13 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
2895 : }
2896 247 : else if (poCT != nullptr)
2897 : {
2898 6 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
2899 : }
2900 :
2901 : /* -------------------------------------------------------------------- */
2902 : /* Create the dataset. */
2903 : /* -------------------------------------------------------------------- */
2904 :
2905 262 : const char *pszAccess =
2906 262 : STARTS_WITH_CI(pszFilename, "/vsisubfile/") ? "r+b" : "w+b";
2907 524 : VSIVirtualHandleUniquePtr fpOwner(VSIFOpenL(pszFilename, pszAccess));
2908 262 : if (!fpOwner)
2909 : {
2910 3 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create file");
2911 3 : CPLFree(localctx.pasBandParams);
2912 3 : localctx.pasBandParams = nullptr;
2913 3 : delete poGMLJP2Box;
2914 3 : return nullptr;
2915 : }
2916 259 : VSILFILE *fp = fpOwner.get();
2917 :
2918 : /* -------------------------------------------------------------------- */
2919 : /* Add JP2 boxes. */
2920 : /* -------------------------------------------------------------------- */
2921 259 : vsi_l_offset nStartJP2C = 0;
2922 259 : int bUseXLBoxes = FALSE;
2923 :
2924 478 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
2925 219 : !BASE::canPerformDirectIO())
2926 : {
2927 438 : GDALJP2Box jPBox(fp);
2928 219 : jPBox.SetType("jP ");
2929 219 : jPBox.AppendWritableData(4, "\x0D\x0A\x87\x0A");
2930 219 : WriteBox(fp, &jPBox);
2931 :
2932 438 : GDALJP2Box ftypBox(fp);
2933 219 : ftypBox.SetType("ftyp");
2934 : // http://docs.opengeospatial.org/is/08-085r5/08-085r5.html Req 19
2935 219 : const bool bJPXOption = CPLFetchBool(papszOptions, "JPX", true);
2936 219 : if (nGMLJP2Version == 2 && bJPXOption)
2937 25 : ftypBox.AppendWritableData(4, "jpx "); /* Branding */
2938 : else
2939 194 : ftypBox.AppendWritableData(4, "jp2 "); /* Branding */
2940 219 : ftypBox.AppendUInt32(0); /* minimum version */
2941 219 : ftypBox.AppendWritableData(
2942 : 4, "jp2 "); /* Compatibility list: first value */
2943 :
2944 219 : if (bInspireTG && poGMLJP2Box != nullptr && !bJPXOption)
2945 : {
2946 1 : CPLError(
2947 : CE_Warning, CPLE_AppDefined,
2948 : "INSPIRE_TG=YES implies following GMLJP2 specification which "
2949 : "recommends advertise reader requirement 67 feature, and thus "
2950 : "JPX capability");
2951 : }
2952 218 : else if (poGMLJP2Box != nullptr && bJPXOption)
2953 : {
2954 : /* GMLJP2 uses lbl and asoc boxes, which are JPEG2000 Part II spec
2955 : */
2956 : /* advertizing jpx is required per 8.1 of 05-047r3 GMLJP2 */
2957 57 : ftypBox.AppendWritableData(
2958 : 4, "jpx "); /* Compatibility list: second value */
2959 : }
2960 219 : WriteBox(fp, &ftypBox);
2961 :
2962 221 : const bool bIPR = poSrcDS->GetMetadata("xml:IPR") != nullptr &&
2963 2 : CPLFetchBool(papszOptions, "WRITE_METADATA", false);
2964 :
2965 : /* Reader requirement box */
2966 219 : if (poGMLJP2Box != nullptr && bJPXOption)
2967 : {
2968 114 : GDALJP2Box rreqBox(fp);
2969 57 : rreqBox.SetType("rreq");
2970 57 : rreqBox.AppendUInt8(1); /* ML = 1 byte for mask length */
2971 :
2972 57 : rreqBox.AppendUInt8(0x80 | 0x40 | (bIPR ? 0x20 : 0)); /* FUAM */
2973 57 : rreqBox.AppendUInt8(0x80); /* DCM */
2974 :
2975 57 : rreqBox.AppendUInt16(
2976 : 2 + (bIPR ? 1 : 0)); /* NSF: Number of standard features */
2977 :
2978 57 : rreqBox.AppendUInt16(
2979 : (bProfile1) ? 4 : 5); /* SF0 : PROFILE 1 or PROFILE 2 */
2980 57 : rreqBox.AppendUInt8(0x80); /* SM0 */
2981 :
2982 57 : rreqBox.AppendUInt16(67); /* SF1 : GMLJP2 box */
2983 57 : rreqBox.AppendUInt8(0x40); /* SM1 */
2984 :
2985 57 : if (bIPR)
2986 : {
2987 0 : rreqBox.AppendUInt16(35); /* SF2 : IPR metadata */
2988 0 : rreqBox.AppendUInt8(0x20); /* SM2 */
2989 : }
2990 57 : rreqBox.AppendUInt16(0); /* NVF */
2991 57 : WriteBox(fp, &rreqBox);
2992 : }
2993 :
2994 438 : GDALJP2Box ihdrBox(fp);
2995 219 : ihdrBox.SetType("ihdr");
2996 219 : ihdrBox.AppendUInt32(nYSize);
2997 219 : ihdrBox.AppendUInt32(nXSize);
2998 219 : ihdrBox.AppendUInt16(static_cast<GUInt16>(nBands));
2999 : GByte BPC;
3000 219 : if (bSamePrecision)
3001 216 : BPC = static_cast<GByte>((localctx.pasBandParams[0].prec - 1) |
3002 216 : (localctx.pasBandParams[0].sgnd << 7));
3003 : else
3004 3 : BPC = 255;
3005 219 : ihdrBox.AppendUInt8(BPC);
3006 219 : ihdrBox.AppendUInt8(7); /* C=Compression type: fixed value */
3007 219 : ihdrBox.AppendUInt8(0); /* UnkC: 0= colourspace of the image is known */
3008 : /*and correctly specified in the Colourspace Specification boxes within
3009 : * the file */
3010 219 : ihdrBox.AppendUInt8(
3011 : bIPR ? 1 : 0); /* IPR: 0=no intellectual property, 1=IPR box */
3012 :
3013 438 : GDALJP2Box bpccBox(fp);
3014 219 : if (!bSamePrecision)
3015 : {
3016 3 : bpccBox.SetType("bpcc");
3017 13 : for (int i = 0; i < nBands; i++)
3018 10 : bpccBox.AppendUInt8(
3019 10 : static_cast<GByte>((localctx.pasBandParams[i].prec - 1) |
3020 10 : (localctx.pasBandParams[i].sgnd << 7)));
3021 : }
3022 :
3023 438 : GDALJP2Box colrBox(fp);
3024 219 : colrBox.SetType("colr");
3025 219 : colrBox.AppendUInt8(1); /* METHOD: 1=Enumerated Colourspace */
3026 219 : colrBox.AppendUInt8(
3027 : 0); /* PREC: Precedence. 0=(field reserved for ISO use) */
3028 219 : colrBox.AppendUInt8(0); /* APPROX: Colourspace approximation. */
3029 219 : GUInt32 enumcs = 16;
3030 219 : if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB))
3031 16 : enumcs = 16;
3032 203 : else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY))
3033 201 : enumcs = 17;
3034 2 : else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC))
3035 2 : enumcs = 18;
3036 219 : colrBox.AppendUInt32(enumcs); /* EnumCS: Enumerated colourspace */
3037 :
3038 438 : GDALJP2Box pclrBox(fp);
3039 438 : GDALJP2Box cmapBox(fp);
3040 219 : int nCTComponentCount = 0;
3041 219 : if (poCT != nullptr)
3042 : {
3043 6 : pclrBox.SetType("pclr");
3044 6 : const int nEntries = std::min(256, poCT->GetColorEntryCount());
3045 : nCTComponentCount =
3046 6 : atoi(CSLFetchNameValueDef(papszOptions, "CT_COMPONENTS", "0"));
3047 6 : if (bInspireTG)
3048 : {
3049 0 : if (nCTComponentCount != 0 && nCTComponentCount != 3)
3050 0 : CPLError(
3051 : CE_Warning, CPLE_AppDefined,
3052 : "Inspire TG mandates 3 components for color table");
3053 : else
3054 0 : nCTComponentCount = 3;
3055 : }
3056 6 : else if (nCTComponentCount != 3 && nCTComponentCount != 4)
3057 : {
3058 5 : nCTComponentCount = 3;
3059 21 : for (int i = 0; i < nEntries; i++)
3060 : {
3061 17 : const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
3062 17 : if (psEntry->c4 != 255)
3063 : {
3064 1 : CPLDebug(
3065 : CODEC::debugId(),
3066 : "Color table has at least one non-opaque value. "
3067 : "This may cause compatibility problems with some "
3068 : "readers. "
3069 : "In which case use CT_COMPONENTS=3 creation "
3070 : "option");
3071 1 : nCTComponentCount = 4;
3072 1 : break;
3073 : }
3074 : }
3075 : }
3076 6 : nRedBandIndex = 0;
3077 6 : nGreenBandIndex = 1;
3078 6 : nBlueBandIndex = 2;
3079 6 : nAlphaBandIndex = (nCTComponentCount == 4) ? 3 : -1;
3080 :
3081 6 : pclrBox.AppendUInt16(static_cast<GUInt16>(nEntries));
3082 6 : pclrBox.AppendUInt8(static_cast<GByte>(
3083 : nCTComponentCount)); /* NPC: Number of components */
3084 25 : for (int i = 0; i < nCTComponentCount; i++)
3085 : {
3086 19 : pclrBox.AppendUInt8(7); /* Bi: unsigned 8 bits */
3087 : }
3088 30 : for (int i = 0; i < nEntries; i++)
3089 : {
3090 24 : const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
3091 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c1));
3092 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c2));
3093 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c3));
3094 24 : if (nCTComponentCount == 4)
3095 4 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c4));
3096 : }
3097 :
3098 6 : cmapBox.SetType("cmap");
3099 25 : for (int i = 0; i < nCTComponentCount; i++)
3100 : {
3101 19 : cmapBox.AppendUInt16(0); /* CMPi: code stream component index */
3102 19 : cmapBox.AppendUInt8(1); /* MYTPi: 1=palette mapping */
3103 19 : cmapBox.AppendUInt8(static_cast<GByte>(
3104 : i)); /* PCOLi: index component from the map */
3105 : }
3106 : }
3107 :
3108 438 : GDALJP2Box cdefBox(fp);
3109 230 : if (((nBands == 3 || nBands == 4) &&
3110 24 : (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
3111 19 : eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
3112 10 : (nRedBandIndex != 0 || nGreenBandIndex != 1 ||
3113 438 : nBlueBandIndex != 2)) ||
3114 : nAlphaBandIndex >= 0)
3115 : {
3116 12 : cdefBox.SetType("cdef");
3117 12 : int nComponents = (nCTComponentCount == 4) ? 4 : nBands;
3118 12 : cdefBox.AppendUInt16(static_cast<GUInt16>(nComponents));
3119 55 : for (int i = 0; i < nComponents; i++)
3120 : {
3121 43 : uint16_t nTyp = 65535; // Unspecified
3122 43 : uint16_t nAsoc = 65535; // Unassociated
3123 43 : if (i != nAlphaBandIndex)
3124 : {
3125 32 : if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
3126 : i == 0)
3127 : {
3128 4 : nTyp =
3129 : 0; // colour image data for the associated colour
3130 4 : nAsoc = 1; // associated with a particular colour
3131 : }
3132 35 : else if ((eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
3133 56 : eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
3134 21 : (nComponents == 3 || nComponents == 4))
3135 : {
3136 24 : nTyp =
3137 : 0; // colour image data for the associated colour
3138 24 : if (i == nRedBandIndex)
3139 8 : nAsoc = 1;
3140 16 : else if (i == nGreenBandIndex)
3141 8 : nAsoc = 2;
3142 8 : else if (i == nBlueBandIndex)
3143 8 : nAsoc = 3;
3144 : else
3145 : {
3146 0 : CPLError(CE_Warning, CPLE_AppDefined,
3147 : "Could not associate band %d with a "
3148 : "red/green/blue channel",
3149 : i + 1);
3150 : }
3151 : }
3152 : }
3153 : else
3154 : {
3155 11 : nTyp = 1; // Non pre-multiplied alpha
3156 11 : nAsoc = 0; // Associated to the image as a whole
3157 : }
3158 :
3159 : // Component number
3160 43 : cdefBox.AppendUInt16(static_cast<GUInt16>(i));
3161 43 : cdefBox.AppendUInt16(nTyp);
3162 43 : cdefBox.AppendUInt16(nAsoc);
3163 : }
3164 : }
3165 :
3166 : // Add res box if needed
3167 219 : GDALJP2Box *poRes = nullptr;
3168 219 : if (poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION") != nullptr &&
3169 224 : poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION") != nullptr &&
3170 5 : poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT") != nullptr)
3171 : {
3172 : double dfXRes =
3173 5 : CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION"));
3174 : double dfYRes =
3175 5 : CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION"));
3176 : int nResUnit =
3177 5 : atoi(poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT"));
3178 : #define PIXELS_PER_INCH 2
3179 : #define PIXELS_PER_CM 3
3180 :
3181 5 : if (nResUnit == PIXELS_PER_INCH)
3182 : {
3183 : // convert pixels per inch to pixels per cm.
3184 2 : dfXRes = dfXRes * 39.37 / 100.0;
3185 2 : dfYRes = dfYRes * 39.37 / 100.0;
3186 2 : nResUnit = PIXELS_PER_CM;
3187 : }
3188 :
3189 5 : if (nResUnit == PIXELS_PER_CM && dfXRes > 0 && dfYRes > 0 &&
3190 5 : dfXRes < 65535 && dfYRes < 65535)
3191 : {
3192 : /* Format a resd box and embed it inside a res box */
3193 10 : GDALJP2Box oResd;
3194 5 : oResd.SetType("resd");
3195 :
3196 5 : int nYDenom = 1;
3197 58 : while (nYDenom < 32767 && dfYRes < 32767)
3198 : {
3199 53 : dfYRes *= 2;
3200 53 : nYDenom *= 2;
3201 : }
3202 5 : int nXDenom = 1;
3203 56 : while (nXDenom < 32767 && dfXRes < 32767)
3204 : {
3205 51 : dfXRes *= 2;
3206 51 : nXDenom *= 2;
3207 : }
3208 :
3209 5 : oResd.AppendUInt16(static_cast<GUInt16>(dfYRes));
3210 5 : oResd.AppendUInt16(static_cast<GUInt16>(nYDenom));
3211 5 : oResd.AppendUInt16(static_cast<GUInt16>(dfXRes));
3212 5 : oResd.AppendUInt16(static_cast<GUInt16>(nXDenom));
3213 5 : oResd.AppendUInt8(2); /* vertical exponent */
3214 5 : oResd.AppendUInt8(2); /* horizontal exponent */
3215 :
3216 5 : GDALJP2Box *poResd = &oResd;
3217 5 : poRes = GDALJP2Box::CreateAsocBox(1, &poResd);
3218 5 : poRes->SetType("res ");
3219 : }
3220 : }
3221 :
3222 : /* Build and write jp2h super box now */
3223 : GDALJP2Box *apoBoxes[7];
3224 219 : int nBoxes = 1;
3225 219 : apoBoxes[0] = &ihdrBox;
3226 219 : if (bpccBox.GetDataLength())
3227 3 : apoBoxes[nBoxes++] = &bpccBox;
3228 219 : apoBoxes[nBoxes++] = &colrBox;
3229 219 : if (pclrBox.GetDataLength())
3230 6 : apoBoxes[nBoxes++] = &pclrBox;
3231 219 : if (cmapBox.GetDataLength())
3232 6 : apoBoxes[nBoxes++] = &cmapBox;
3233 219 : if (cdefBox.GetDataLength())
3234 12 : apoBoxes[nBoxes++] = &cdefBox;
3235 219 : if (poRes)
3236 5 : apoBoxes[nBoxes++] = poRes;
3237 : GDALJP2Box *psJP2HBox =
3238 219 : GDALJP2Box::CreateSuperBox("jp2h", nBoxes, apoBoxes);
3239 219 : WriteBox(fp, psJP2HBox);
3240 219 : delete psJP2HBox;
3241 219 : delete poRes;
3242 :
3243 219 : if (!bGeoBoxesAfter)
3244 : {
3245 208 : if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
3246 : {
3247 150 : GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
3248 150 : WriteBox(fp, poBox);
3249 150 : delete poBox;
3250 : }
3251 :
3252 219 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
3253 11 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3254 : {
3255 11 : WriteXMPBox(fp, poSrcDS);
3256 : }
3257 :
3258 208 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3259 : {
3260 11 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3261 11 : WriteXMLBoxes(fp, poSrcDS);
3262 11 : WriteGDALMetadataBox(fp, poSrcDS, papszOptions);
3263 : }
3264 :
3265 208 : if (poGMLJP2Box != nullptr)
3266 : {
3267 53 : WriteBox(fp, poGMLJP2Box);
3268 : }
3269 : }
3270 : }
3271 :
3272 : /* -------------------------------------------------------------------- */
3273 : /* Try lossless reuse of an existing JPEG2000 codestream */
3274 : /* -------------------------------------------------------------------- */
3275 259 : vsi_l_offset nCodeStreamLength = 0;
3276 259 : vsi_l_offset nCodeStreamStart = 0;
3277 259 : VSILFILE *fpSrc = nullptr;
3278 259 : if (CPLFetchBool(papszOptions, "USE_SRC_CODESTREAM", false))
3279 : {
3280 14 : CPLString osSrcFilename(poSrcDS->GetDescription());
3281 14 : if (poSrcDS->GetDriver() != nullptr &&
3282 7 : poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
3283 : {
3284 0 : VRTDataset *poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
3285 0 : if (poVRTDS)
3286 : {
3287 : GDALDataset *poSimpleSourceDS =
3288 0 : poVRTDS->GetSingleSimpleSource();
3289 0 : if (poSimpleSourceDS)
3290 0 : osSrcFilename = poSimpleSourceDS->GetDescription();
3291 : }
3292 : }
3293 :
3294 7 : fpSrc = VSIFOpenL(osSrcFilename, "rb");
3295 7 : if (fpSrc)
3296 : {
3297 7 : nCodeStreamStart = JP2FindCodeStream(fpSrc, &nCodeStreamLength);
3298 : }
3299 7 : if (nCodeStreamLength == 0)
3300 : {
3301 1 : CPLError(
3302 : CE_Warning, CPLE_AppDefined,
3303 : "USE_SRC_CODESTREAM=YES specified, but no codestream found");
3304 : }
3305 : }
3306 :
3307 478 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
3308 219 : !BASE::canPerformDirectIO())
3309 : {
3310 : // Start codestream box
3311 219 : nStartJP2C = VSIFTellL(fp);
3312 219 : if (nCodeStreamLength)
3313 6 : bUseXLBoxes = nCodeStreamLength > UINT_MAX;
3314 : else
3315 426 : bUseXLBoxes = CPLFetchBool(papszOptions, "JP2C_XLBOX",
3316 425 : false) || /* For debugging */
3317 212 : static_cast<GIntBig>(nXSize) * nYSize * nBands *
3318 212 : nDataTypeSize / adfRates.back() >
3319 : 4e9;
3320 219 : GUInt32 nLBox = (bUseXLBoxes) ? 1 : 0;
3321 219 : CPL_MSBPTR32(&nLBox);
3322 219 : VSIFWriteL(&nLBox, 1, 4, fp);
3323 219 : VSIFWriteL("jp2c", 1, 4, fp);
3324 219 : if (bUseXLBoxes)
3325 : {
3326 1 : GUIntBig nXLBox = 0;
3327 1 : VSIFWriteL(&nXLBox, 1, 8, fp);
3328 : }
3329 : }
3330 :
3331 : /* -------------------------------------------------------------------- */
3332 : /* Do lossless reuse of an existing JPEG2000 codestream */
3333 : /* -------------------------------------------------------------------- */
3334 259 : if (fpSrc)
3335 : {
3336 7 : const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
3337 : "BLOCKYSIZE",
3338 : "QUALITY",
3339 : "REVERSIBLE",
3340 : "RESOLUTIONS",
3341 : "PROGRESSION",
3342 : "SOP",
3343 : "EPH",
3344 : "YCBCR420",
3345 : "YCC",
3346 : "NBITS",
3347 : "1BIT_ALPHA",
3348 : "PRECINCTS",
3349 : "TILEPARTS",
3350 : "CODEBLOCK_WIDTH",
3351 : "CODEBLOCK_HEIGHT",
3352 : "PLT",
3353 : nullptr};
3354 126 : for (int i = 0; apszIgnoredOptions[i]; i++)
3355 : {
3356 119 : if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
3357 : {
3358 1 : CPLError(CE_Warning, CPLE_NotSupported,
3359 : "Option %s ignored when USE_SRC_CODESTREAM=YES",
3360 : apszIgnoredOptions[i]);
3361 : }
3362 : }
3363 : GByte abyBuffer[4096];
3364 7 : VSIFSeekL(fpSrc, nCodeStreamStart, SEEK_SET);
3365 7 : vsi_l_offset nRead = 0;
3366 : /* coverity[tainted_data] */
3367 17 : while (nRead < nCodeStreamLength)
3368 : {
3369 10 : const size_t nToRead =
3370 10 : (nCodeStreamLength - nRead > 4096)
3371 : ? 4096
3372 : : static_cast<size_t>(nCodeStreamLength - nRead);
3373 10 : if (VSIFReadL(abyBuffer, 1, nToRead, fpSrc) != nToRead)
3374 : {
3375 0 : VSIFCloseL(fpSrc);
3376 0 : delete poGMLJP2Box;
3377 0 : return nullptr;
3378 : }
3379 10 : if (nRead == 0 && (pszProfile || bInspireTG) &&
3380 6 : abyBuffer[2] == 0xFF && abyBuffer[3] == 0x51)
3381 : {
3382 6 : if (EQUAL(pszProfile, "UNRESTRICTED"))
3383 : {
3384 0 : abyBuffer[6] = 0;
3385 0 : abyBuffer[7] = 0;
3386 : }
3387 6 : else if (EQUAL(pszProfile, "PROFILE_1") || bInspireTG)
3388 : {
3389 : // TODO: ultimately we should check that we can really set
3390 : // Profile 1
3391 1 : abyBuffer[6] = 0;
3392 1 : abyBuffer[7] = 2;
3393 : }
3394 : }
3395 20 : if (VSIFWriteL(abyBuffer, 1, nToRead, fp) != nToRead ||
3396 10 : !pfnProgress((nRead + nToRead) * 1.0 / nCodeStreamLength,
3397 : nullptr, pProgressData))
3398 : {
3399 0 : VSIFCloseL(fpSrc);
3400 0 : delete poGMLJP2Box;
3401 0 : return nullptr;
3402 : }
3403 10 : nRead += nToRead;
3404 : }
3405 :
3406 7 : VSIFCloseL(fpSrc);
3407 : }
3408 : else
3409 : {
3410 252 : localctx.open(fp);
3411 252 : if (!localctx.initCompress(papszOptions, adfRates, nBlockXSize,
3412 : nBlockYSize, bIsIrreversible,
3413 : nNumResolutions, eProgOrder, bYCC, nCblockW,
3414 : nCblockH, bYCBCR420, bProfile1, nBands,
3415 : nXSize, nYSize, eColorSpace, numThreads))
3416 : {
3417 0 : CPLError(CE_Failure, CPLE_AppDefined, "init compress failed");
3418 0 : localctx.free();
3419 0 : delete poGMLJP2Box;
3420 11 : return nullptr;
3421 : }
3422 :
3423 : /* Grok JP2: let codec handle box writing natively */
3424 252 : if (BASE::canPerformDirectIO() &&
3425 0 : eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
3426 : {
3427 0 : localctx.setupJP2Metadata(
3428 : bInspireTG, bProfile1, bGeoBoxesAfter,
3429 0 : (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2) ? &oJP2MD
3430 : : nullptr,
3431 : poGMLJP2Box, nAlphaBandIndex, nRedBandIndex, nGreenBandIndex,
3432 : nBlueBandIndex, eColorSpace, nBands, poCT, poSrcDS,
3433 : papszOptions);
3434 : }
3435 :
3436 252 : if (!localctx.initCodec(pszFilename, fpOwner))
3437 : {
3438 0 : CPLError(CE_Failure, CPLE_AppDefined,
3439 : "codec initialization failed");
3440 0 : localctx.free();
3441 0 : delete poGMLJP2Box;
3442 0 : return nullptr;
3443 : }
3444 252 : if (!fpOwner)
3445 0 : fp = nullptr;
3446 :
3447 252 : const int nTilesX = DIV_ROUND_UP(nXSize, nBlockXSize);
3448 252 : const int nTilesY = DIV_ROUND_UP(nYSize, nBlockYSize);
3449 :
3450 252 : const GUIntBig nTileSize = static_cast<GUIntBig>(nBlockXSize) *
3451 252 : nBlockYSize * nBands * nDataTypeSize;
3452 252 : GByte *pTempBuffer = nullptr;
3453 :
3454 252 : const bool bUseIOThread =
3455 504 : CODEC::preferPerTileCompress() && (nTilesX > 1 || nTilesY > 1) &&
3456 11 : nTileSize < 10 * 1024 * 1024 &&
3457 515 : strcmp(CPLGetThreadingModel(), "stub") != 0 &&
3458 11 : CPLTestBool(
3459 : CPLGetConfigOption("JP2OPENJPEG_USE_THREADED_IO", "YES"));
3460 :
3461 252 : if (nTileSize > UINT_MAX)
3462 : {
3463 1 : CPLError(CE_Failure, CPLE_NotSupported, "Tile size exceeds 4GB");
3464 1 : pTempBuffer = nullptr;
3465 : }
3466 : else
3467 : {
3468 : // Double memory buffer when using threaded I/O
3469 251 : const size_t nBufferSize =
3470 : static_cast<size_t>(bUseIOThread ? nTileSize * 2 : nTileSize);
3471 251 : pTempBuffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBufferSize));
3472 : }
3473 252 : if (pTempBuffer == nullptr)
3474 : {
3475 1 : localctx.free();
3476 1 : delete poGMLJP2Box;
3477 1 : return nullptr;
3478 : }
3479 :
3480 251 : GByte *pYUV420Buffer = nullptr;
3481 251 : if (bYCBCR420)
3482 : {
3483 2 : pYUV420Buffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(
3484 : nBlockXSize * nBlockYSize + nBlockXSize * nBlockYSize / 2 +
3485 : ((nBands == 4) ? nBlockXSize * nBlockYSize : 0)));
3486 2 : if (pYUV420Buffer == nullptr)
3487 : {
3488 0 : localctx.free();
3489 0 : CPLFree(pTempBuffer);
3490 0 : delete poGMLJP2Box;
3491 0 : return nullptr;
3492 : }
3493 : }
3494 :
3495 : /* --------------------------------------------------------------------
3496 : */
3497 : /* Iterate over the tiles */
3498 : /* --------------------------------------------------------------------
3499 : */
3500 251 : pfnProgress(0.0, nullptr, pProgressData);
3501 :
3502 : struct ReadRasterJob
3503 : {
3504 : GDALDataset *poSrcDS;
3505 : int nXOff;
3506 : int nYOff;
3507 : int nWidthToRead;
3508 : int nHeightToRead;
3509 : GDALDataType eDataType;
3510 : GByte *pBuffer;
3511 : int nBands;
3512 : CPLErr eErr;
3513 : };
3514 :
3515 814 : const auto ReadRasterFunction = [](void *threadData)
3516 : {
3517 407 : ReadRasterJob *job = static_cast<ReadRasterJob *>(threadData);
3518 814 : job->eErr = job->poSrcDS->RasterIO(
3519 : GF_Read, job->nXOff, job->nYOff, job->nWidthToRead,
3520 407 : job->nHeightToRead, job->pBuffer, job->nWidthToRead,
3521 : job->nHeightToRead, job->eDataType, job->nBands, nullptr, 0, 0,
3522 : 0, nullptr);
3523 : };
3524 :
3525 251 : CPLWorkerThreadPool oPool;
3526 251 : if (bUseIOThread)
3527 : {
3528 10 : oPool.Setup(1, nullptr, nullptr);
3529 : }
3530 :
3531 251 : GByte *pabyActiveBuffer = pTempBuffer;
3532 251 : GByte *pabyBackgroundBuffer =
3533 251 : pTempBuffer + static_cast<size_t>(nTileSize);
3534 :
3535 251 : CPLErr eErr = CE_None;
3536 251 : int iTile = 0;
3537 :
3538 : ReadRasterJob job;
3539 251 : job.eDataType = eDataType;
3540 251 : job.pBuffer = pabyActiveBuffer;
3541 251 : job.nBands = nBands;
3542 251 : job.eErr = CE_Failure;
3543 251 : job.poSrcDS = poSrcDS;
3544 :
3545 251 : if (bUseIOThread)
3546 : {
3547 10 : job.nXOff = 0;
3548 10 : job.nYOff = 0;
3549 10 : job.nWidthToRead = std::min(nBlockXSize, nXSize);
3550 10 : job.nHeightToRead = std::min(nBlockYSize, nYSize);
3551 10 : job.pBuffer = pabyBackgroundBuffer;
3552 10 : ReadRasterFunction(&job);
3553 10 : eErr = job.eErr;
3554 : }
3555 :
3556 526 : for (int nBlockYOff = 0; eErr == CE_None && nBlockYOff < nTilesY;
3557 : nBlockYOff++)
3558 : {
3559 682 : for (int nBlockXOff = 0; eErr == CE_None && nBlockXOff < nTilesX;
3560 : nBlockXOff++)
3561 : {
3562 407 : const int nWidthToRead =
3563 407 : std::min(nBlockXSize, nXSize - nBlockXOff * nBlockXSize);
3564 407 : const int nHeightToRead =
3565 407 : std::min(nBlockYSize, nYSize - nBlockYOff * nBlockYSize);
3566 :
3567 407 : if (bUseIOThread)
3568 : {
3569 : // Wait for previous background I/O task to be finished
3570 100 : oPool.WaitCompletion();
3571 100 : eErr = job.eErr;
3572 :
3573 : // Swap buffers
3574 100 : std::swap(pabyBackgroundBuffer, pabyActiveBuffer);
3575 :
3576 : // Prepare for next I/O task
3577 100 : int nNextBlockXOff = nBlockXOff + 1;
3578 100 : int nNextBlockYOff = nBlockYOff;
3579 100 : if (nNextBlockXOff == nTilesX)
3580 : {
3581 26 : nNextBlockXOff = 0;
3582 26 : nNextBlockYOff++;
3583 : }
3584 100 : if (nNextBlockYOff != nTilesY)
3585 : {
3586 90 : job.nXOff = nNextBlockXOff * nBlockXSize;
3587 90 : job.nYOff = nNextBlockYOff * nBlockYSize;
3588 90 : job.nWidthToRead =
3589 90 : std::min(nBlockXSize, nXSize - job.nXOff);
3590 90 : job.nHeightToRead =
3591 90 : std::min(nBlockYSize, nYSize - job.nYOff);
3592 90 : job.pBuffer = pabyBackgroundBuffer;
3593 :
3594 : // Submit next job
3595 90 : oPool.SubmitJob(ReadRasterFunction, &job);
3596 : }
3597 : }
3598 : else
3599 : {
3600 307 : job.nXOff = nBlockXOff * nBlockXSize;
3601 307 : job.nYOff = nBlockYOff * nBlockYSize;
3602 307 : job.nWidthToRead = nWidthToRead;
3603 307 : job.nHeightToRead = nHeightToRead;
3604 307 : ReadRasterFunction(&job);
3605 307 : eErr = job.eErr;
3606 : }
3607 :
3608 407 : if (b1BitAlpha)
3609 : {
3610 64987 : for (int i = 0; i < nWidthToRead * nHeightToRead; i++)
3611 : {
3612 64984 : if (pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3613 64984 : nHeightToRead +
3614 : i])
3615 25040 : pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3616 25040 : nHeightToRead +
3617 25040 : i] = 1;
3618 : else
3619 39944 : pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3620 39944 : nHeightToRead +
3621 39944 : i] = 0;
3622 : }
3623 : }
3624 407 : if (eErr == CE_None)
3625 : {
3626 407 : if (bYCBCR420)
3627 : {
3628 202 : for (int j = 0; j < nHeightToRead; j++)
3629 : {
3630 27000 : for (int i = 0; i < nWidthToRead; i++)
3631 : {
3632 26800 : const int R =
3633 26800 : pabyActiveBuffer[j * nWidthToRead + i];
3634 26800 : const int G =
3635 26800 : pabyActiveBuffer[nHeightToRead *
3636 26800 : nWidthToRead +
3637 26800 : j * nWidthToRead + i];
3638 26800 : const int B =
3639 26800 : pabyActiveBuffer[2 * nHeightToRead *
3640 26800 : nWidthToRead +
3641 26800 : j * nWidthToRead + i];
3642 26800 : const int Y = static_cast<int>(
3643 26800 : 0.299 * R + 0.587 * G + 0.114 * B);
3644 53600 : const int Cb = CLAMP_0_255(static_cast<int>(
3645 26800 : -0.1687 * R - 0.3313 * G + 0.5 * B + 128));
3646 53600 : const int Cr = CLAMP_0_255(static_cast<int>(
3647 26800 : 0.5 * R - 0.4187 * G - 0.0813 * B + 128));
3648 26800 : pYUV420Buffer[j * nWidthToRead + i] =
3649 : static_cast<GByte>(Y);
3650 26800 : pYUV420Buffer[nHeightToRead * nWidthToRead +
3651 26800 : ((j / 2) * ((nWidthToRead) / 2) +
3652 26800 : i / 2)] = static_cast<GByte>(Cb);
3653 26800 : pYUV420Buffer[5 * nHeightToRead * nWidthToRead /
3654 26800 : 4 +
3655 26800 : ((j / 2) * ((nWidthToRead) / 2) +
3656 26800 : i / 2)] = static_cast<GByte>(Cr);
3657 26800 : if (nBands == 4)
3658 : {
3659 24300 : pYUV420Buffer[3 * nHeightToRead *
3660 24300 : nWidthToRead / 2 +
3661 24300 : j * nWidthToRead + i] =
3662 24300 : static_cast<GByte>(
3663 24300 : pabyActiveBuffer[3 * nHeightToRead *
3664 24300 : nWidthToRead +
3665 24300 : j * nWidthToRead +
3666 : i]);
3667 : }
3668 : }
3669 : }
3670 :
3671 2 : int nBytesToWrite =
3672 2 : 3 * nWidthToRead * nHeightToRead / 2;
3673 2 : if (nBands == 4)
3674 1 : nBytesToWrite += nBlockXSize * nBlockYSize;
3675 :
3676 2 : if (!localctx.compressTile(iTile, pYUV420Buffer,
3677 : nBytesToWrite))
3678 : {
3679 0 : CPLError(CE_Failure, CPLE_AppDefined,
3680 : "compress tile failed");
3681 0 : eErr = CE_Failure;
3682 : }
3683 : }
3684 : else
3685 : {
3686 405 : if (!localctx.compressTile(iTile, pabyActiveBuffer,
3687 405 : nWidthToRead *
3688 405 : nHeightToRead * nBands *
3689 : nDataTypeSize))
3690 : {
3691 0 : CPLError(CE_Failure, CPLE_AppDefined,
3692 : "compress tile failed");
3693 0 : eErr = CE_Failure;
3694 : }
3695 : }
3696 : }
3697 :
3698 407 : if (!pfnProgress((iTile + 1) * 1.0 / (nTilesX * nTilesY),
3699 : nullptr, pProgressData))
3700 0 : eErr = CE_Failure;
3701 :
3702 407 : iTile++;
3703 : }
3704 : }
3705 :
3706 251 : if (bUseIOThread && eErr == CE_Failure)
3707 : {
3708 : // Wait for previous background I/O task to be finished
3709 : // before freeing buffers (pTempBuffer, etc.)
3710 0 : oPool.WaitCompletion();
3711 : }
3712 :
3713 251 : VSIFree(pTempBuffer);
3714 251 : VSIFree(pYUV420Buffer);
3715 :
3716 251 : if (eErr != CE_None)
3717 : {
3718 0 : localctx.free();
3719 0 : delete poGMLJP2Box;
3720 0 : return nullptr;
3721 : }
3722 :
3723 251 : if (!localctx.finishCompress())
3724 : {
3725 10 : localctx.free();
3726 10 : delete poGMLJP2Box;
3727 10 : return nullptr;
3728 : }
3729 241 : localctx.free();
3730 : }
3731 :
3732 : /* -------------------------------------------------------------------- */
3733 : /* Patch JP2C box length and add trailing JP2 boxes */
3734 : /* -------------------------------------------------------------------- */
3735 248 : bool bRet = true;
3736 248 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
3737 466 : !BASE::canPerformDirectIO() &&
3738 218 : !CPLFetchBool(papszOptions, "JP2C_LENGTH_ZERO",
3739 : false) /* debug option */)
3740 : {
3741 217 : vsi_l_offset nEndJP2C = VSIFTellL(fp);
3742 217 : GUIntBig nBoxSize = nEndJP2C - nStartJP2C;
3743 217 : if (bUseXLBoxes)
3744 : {
3745 1 : VSIFSeekL(fp, nStartJP2C + 8, SEEK_SET);
3746 1 : CPL_MSBPTR64(&nBoxSize);
3747 1 : if (VSIFWriteL(&nBoxSize, 8, 1, fp) != 1)
3748 0 : bRet = false;
3749 : }
3750 : else
3751 : {
3752 216 : if (nBoxSize > UINT_MAX)
3753 : {
3754 : /* Should not happen hopefully */
3755 0 : if ((bGeoreferencingCompatOfGeoJP2 || poGMLJP2Box) &&
3756 : bGeoBoxesAfter)
3757 : {
3758 0 : CPLError(CE_Warning, CPLE_AppDefined,
3759 : "Cannot write GMLJP2/GeoJP2 boxes as codestream "
3760 : "is unexpectedly > 4GB");
3761 0 : bGeoreferencingCompatOfGeoJP2 = FALSE;
3762 0 : delete poGMLJP2Box;
3763 0 : poGMLJP2Box = nullptr;
3764 : }
3765 : }
3766 : else
3767 : {
3768 216 : VSIFSeekL(fp, nStartJP2C, SEEK_SET);
3769 216 : GUInt32 nBoxSize32 = static_cast<GUInt32>(nBoxSize);
3770 216 : CPL_MSBPTR32(&nBoxSize32);
3771 216 : if (VSIFWriteL(&nBoxSize32, 4, 1, fp) != 1)
3772 0 : bRet = false;
3773 : }
3774 : }
3775 217 : VSIFSeekL(fp, 0, SEEK_END);
3776 :
3777 217 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3778 : {
3779 14 : if (!WriteIPRBox(fp, poSrcDS))
3780 0 : bRet = false;
3781 : }
3782 :
3783 217 : if (bGeoBoxesAfter)
3784 : {
3785 11 : if (poGMLJP2Box != nullptr)
3786 : {
3787 5 : if (!WriteBox(fp, poGMLJP2Box))
3788 0 : bRet = false;
3789 : }
3790 :
3791 11 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3792 : {
3793 3 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3794 : {
3795 3 : if (!WriteXMLBoxes(fp, poSrcDS))
3796 0 : bRet = false;
3797 : }
3798 3 : if (!WriteGDALMetadataBox(fp, poSrcDS, papszOptions))
3799 0 : bRet = false;
3800 : }
3801 :
3802 11 : if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
3803 : {
3804 5 : GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
3805 5 : if (!WriteBox(fp, poBox))
3806 0 : bRet = false;
3807 5 : delete poBox;
3808 : }
3809 :
3810 14 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
3811 3 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3812 : {
3813 3 : if (!WriteXMPBox(fp, poSrcDS))
3814 0 : bRet = false;
3815 : }
3816 : }
3817 : }
3818 :
3819 248 : if (fpOwner)
3820 : {
3821 248 : if (VSIFCloseL(fpOwner.release()) != 0)
3822 0 : bRet = false;
3823 : }
3824 248 : delete poGMLJP2Box;
3825 248 : if (!bRet)
3826 0 : return nullptr;
3827 :
3828 : /* -------------------------------------------------------------------- */
3829 : /* Re-open dataset, and copy any auxiliary pam information. */
3830 : /* -------------------------------------------------------------------- */
3831 :
3832 248 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
3833 3 : auto poDS =
3834 248 : dynamic_cast<JP2OPJLikeDataset *>(JP2OPJLikeDataset::Open(&oOpenInfo));
3835 :
3836 248 : if (poDS)
3837 : {
3838 245 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT & (~GCIF_METADATA));
3839 :
3840 : /* Only write relevant metadata to PAM, and if needed */
3841 245 : if (!CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3842 : {
3843 231 : char **papszSrcMD = CSLDuplicate(poSrcDS->GetMetadata());
3844 : papszSrcMD =
3845 231 : CSLSetNameValue(papszSrcMD, GDALMD_AREA_OR_POINT, nullptr);
3846 231 : papszSrcMD = CSLSetNameValue(papszSrcMD, "Corder", nullptr);
3847 322 : for (char **papszSrcMDIter = papszSrcMD;
3848 322 : papszSrcMDIter && *papszSrcMDIter;)
3849 : {
3850 : /* Remove entries like KEY= (without value) */
3851 91 : if ((*papszSrcMDIter)[0] &&
3852 91 : (*papszSrcMDIter)[strlen((*papszSrcMDIter)) - 1] == '=')
3853 : {
3854 37 : CPLFree(*papszSrcMDIter);
3855 37 : memmove(papszSrcMDIter, papszSrcMDIter + 1,
3856 : sizeof(char *) *
3857 37 : (CSLCount(papszSrcMDIter + 1) + 1));
3858 : }
3859 : else
3860 54 : ++papszSrcMDIter;
3861 : }
3862 231 : char **papszMD = CSLDuplicate(poDS->GetMetadata());
3863 231 : papszMD = CSLSetNameValue(papszMD, GDALMD_AREA_OR_POINT, nullptr);
3864 246 : if (papszSrcMD && papszSrcMD[0] != nullptr &&
3865 15 : CSLCount(papszSrcMD) != CSLCount(papszMD))
3866 : {
3867 9 : poDS->SetMetadata(papszSrcMD);
3868 : }
3869 231 : CSLDestroy(papszSrcMD);
3870 231 : CSLDestroy(papszMD);
3871 : }
3872 : }
3873 :
3874 248 : return poDS;
3875 : }
3876 :
3877 : #ifdef unused
3878 : template <typename CODEC, typename BASE>
3879 : void GDALRegisterJP2(const std::string &libraryName,
3880 : const std::string &driverName)
3881 :
3882 : {
3883 : if (!GDAL_CHECK_VERSION((driverName + " driver").c_str()))
3884 : return;
3885 :
3886 : if (GDALGetDriverByName(driverName.c_str()) != nullptr)
3887 : return;
3888 :
3889 : GDALDriver *poDriver = new GDALDriver();
3890 : poDriver->SetDescription(driverName.c_str());
3891 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3892 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
3893 : poDriver->SetMetadataItem(
3894 : GDAL_DMD_LONGNAME,
3895 : ("JPEG-2000 driver based on " + libraryName + " library").c_str());
3896 :
3897 : poDriver->SetMetadataItem(
3898 : GDAL_DMD_HELPTOPIC,
3899 : ("drivers/raster/jp2" + CPLString(libraryName).tolower() + ".html")
3900 : .c_str());
3901 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jp2");
3902 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jp2");
3903 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "jp2 j2k");
3904 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
3905 : "Byte Int16 UInt16 Int32 UInt32");
3906 :
3907 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3908 : BASE::setMetaData(poDriver);
3909 :
3910 : poDriver->pfnIdentify = JP2OPJLikeDataset<CODEC, BASE>::Identify;
3911 : poDriver->pfnOpen = JP2OPJLikeDataset<CODEC, BASE>::Open;
3912 : poDriver->pfnCreateCopy = JP2OPJLikeDataset<CODEC, BASE>::CreateCopy;
3913 :
3914 : GetGDALDriverManager()->RegisterDriver(poDriver);
3915 : }
3916 : #endif
|