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 24 : GDALRasterBand *JP2OPJLikeRasterBand<CODEC, BASE>::GetOverview(int iOvrLevel)
776 : {
777 24 : if (GDALPamRasterBand::GetOverviewCount() > 0)
778 6 : return GDALPamRasterBand::GetOverview(iOvrLevel);
779 :
780 18 : auto poGDS = cpl::down_cast<JP2OPJLikeDataset<CODEC, BASE> *>(poDS);
781 18 : if (iOvrLevel < 0 || iOvrLevel >= poGDS->nOverviewCount)
782 0 : return nullptr;
783 :
784 18 : 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 : // For Grok's DirectRasterIO path, overview datasets need tile
1910 : // dimensions scaled to the reduced resolution level so that
1911 : // tile range and row-iteration computations work correctly.
1912 117 : if (BASE::canPerformDirectIO())
1913 : {
1914 0 : const int ovLevel = poODS->iLevel;
1915 0 : poODS->m_nTileWidth = (nTileW + (1 << ovLevel) - 1) >> ovLevel;
1916 0 : poODS->m_nTileHeight = (nTileH + (1 << ovLevel) - 1) >> ovLevel;
1917 : }
1918 :
1919 335 : for (iBand = 1; iBand <= poDS->nBands; iBand++)
1920 : {
1921 218 : const bool bPromoteTo8Bit =
1922 237 : iBand == poDS->nAlphaIndex + 1 &&
1923 19 : localctx.psImage
1924 19 : ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1)
1925 : ? 1
1926 : : 0]
1927 19 : .prec == 8 &&
1928 246 : localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
1929 9 : CPLFetchBool(
1930 9 : poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
1931 9 : CPLTestBool(CPLGetConfigOption(
1932 : "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
1933 :
1934 218 : poODS->SetBand(iBand,
1935 430 : new JP2OPJLikeRasterBand<CODEC, BASE>(
1936 : poODS, iBand, eDataType,
1937 : bPromoteTo8Bit
1938 : ? 8
1939 212 : : localctx.psImage->comps[iBand - 1].prec,
1940 : bPromoteTo8Bit, nBlockXSize, nBlockYSize));
1941 : }
1942 :
1943 117 : poDS->papoOverviewDS[poDS->nOverviewCount++] = poODS;
1944 : }
1945 :
1946 760 : poDS->openCompleteJP2(&localctx);
1947 :
1948 : /* -------------------------------------------------------------------- */
1949 : /* More metadata. */
1950 : /* -------------------------------------------------------------------- */
1951 760 : if (poDS->nBands > 1)
1952 : {
1953 73 : poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
1954 : "IMAGE_STRUCTURE");
1955 : }
1956 :
1957 760 : poOpenInfo->fpL = poDS->fp_;
1958 760 : vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
1959 760 : poDS->LoadJP2Metadata(poOpenInfo);
1960 760 : VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
1961 760 : poOpenInfo->fpL = nullptr;
1962 :
1963 760 : poDS->bHasGeoreferencingAtOpening =
1964 996 : (!poDS->m_oSRS.IsEmpty() || poDS->nGCPCount != 0 ||
1965 236 : poDS->bGeoTransformValid);
1966 :
1967 : /* -------------------------------------------------------------------- */
1968 : /* Vector layers */
1969 : /* -------------------------------------------------------------------- */
1970 760 : if (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
1971 : {
1972 66 : poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
1973 : "OPEN_REMOTE_GML", false));
1974 :
1975 : // If file opened in vector-only mode and there's no vector,
1976 : // return
1977 70 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
1978 4 : poDS->GetLayerCount() == 0)
1979 : {
1980 2 : delete poDS;
1981 2 : return nullptr;
1982 : }
1983 : }
1984 :
1985 : /* -------------------------------------------------------------------- */
1986 : /* Initialize any PAM information. */
1987 : /* -------------------------------------------------------------------- */
1988 758 : poDS->SetDescription(poOpenInfo->pszFilename);
1989 758 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
1990 :
1991 : /* -------------------------------------------------------------------- */
1992 : /* Check for overviews. */
1993 : /* -------------------------------------------------------------------- */
1994 758 : poDS->oOvManager.Initialize(poDS, poOpenInfo);
1995 :
1996 758 : return poDS;
1997 : }
1998 :
1999 : /************************************************************************/
2000 : /* WriteBox() */
2001 : /************************************************************************/
2002 :
2003 : template <typename CODEC, typename BASE>
2004 942 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteBox(VSILFILE *fp, GDALJP2Box *poBox)
2005 : {
2006 : GUInt32 nLBox;
2007 : GUInt32 nTBox;
2008 :
2009 942 : if (poBox == nullptr)
2010 0 : return true;
2011 :
2012 942 : nLBox = static_cast<int>(poBox->GetDataLength()) + 8;
2013 942 : nLBox = CPL_MSBWORD32(nLBox);
2014 :
2015 942 : memcpy(&nTBox, poBox->GetType(), 4);
2016 :
2017 942 : return VSIFWriteL(&nLBox, 4, 1, fp) == 1 &&
2018 1884 : VSIFWriteL(&nTBox, 4, 1, fp) == 1 &&
2019 942 : VSIFWriteL(poBox->GetWritableData(),
2020 1884 : static_cast<int>(poBox->GetDataLength()), 1, fp) == 1;
2021 : }
2022 :
2023 : /************************************************************************/
2024 : /* WriteGDALMetadataBox() */
2025 : /************************************************************************/
2026 :
2027 : template <typename CODEC, typename BASE>
2028 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteGDALMetadataBox(
2029 : VSILFILE *fp, GDALDataset *poSrcDS, CSLConstList papszOptions)
2030 : {
2031 19 : bool bRet = true;
2032 38 : GDALJP2Box *poBox = GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
2033 19 : poSrcDS, CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false));
2034 19 : if (poBox)
2035 6 : bRet = WriteBox(fp, poBox);
2036 19 : delete poBox;
2037 19 : return bRet;
2038 : }
2039 :
2040 : /************************************************************************/
2041 : /* WriteXMLBoxes() */
2042 : /************************************************************************/
2043 :
2044 : template <typename CODEC, typename BASE>
2045 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMLBoxes(VSILFILE *fp,
2046 : GDALDataset *poSrcDS)
2047 : {
2048 19 : bool bRet = true;
2049 19 : int nBoxes = 0;
2050 19 : GDALJP2Box **papoBoxes = GDALJP2Metadata::CreateXMLBoxes(poSrcDS, &nBoxes);
2051 21 : for (int i = 0; i < nBoxes; i++)
2052 : {
2053 2 : if (!WriteBox(fp, papoBoxes[i]))
2054 0 : bRet = false;
2055 2 : delete papoBoxes[i];
2056 : }
2057 19 : CPLFree(papoBoxes);
2058 19 : return bRet;
2059 : }
2060 :
2061 : /************************************************************************/
2062 : /* WriteXMPBox() */
2063 : /************************************************************************/
2064 :
2065 : template <typename CODEC, typename BASE>
2066 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMPBox(VSILFILE *fp,
2067 : GDALDataset *poSrcDS)
2068 : {
2069 19 : bool bRet = true;
2070 19 : GDALJP2Box *poBox = GDALJP2Metadata::CreateXMPBox(poSrcDS);
2071 19 : if (poBox)
2072 2 : bRet = WriteBox(fp, poBox);
2073 19 : delete poBox;
2074 19 : return bRet;
2075 : }
2076 :
2077 : /************************************************************************/
2078 : /* WriteIPRBox() */
2079 : /************************************************************************/
2080 :
2081 : template <typename CODEC, typename BASE>
2082 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteIPRBox(VSILFILE *fp,
2083 : GDALDataset *poSrcDS)
2084 : {
2085 19 : bool bRet = true;
2086 19 : GDALJP2Box *poBox = GDALJP2Metadata::CreateIPRBox(poSrcDS);
2087 19 : if (poBox)
2088 2 : bRet = WriteBox(fp, poBox);
2089 19 : delete poBox;
2090 19 : return bRet;
2091 : }
2092 :
2093 : /************************************************************************/
2094 : /* FloorPowerOfTwo() */
2095 : /************************************************************************/
2096 :
2097 544 : static int FloorPowerOfTwo(int nVal)
2098 : {
2099 544 : int nBits = 0;
2100 3805 : while (nVal > 1)
2101 : {
2102 3261 : nBits++;
2103 3261 : nVal >>= 1;
2104 : }
2105 544 : return 1 << nBits;
2106 : }
2107 :
2108 : /************************************************************************/
2109 : /* CreateCopy() */
2110 : /************************************************************************/
2111 :
2112 : template <typename CODEC, typename BASE>
2113 282 : GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::CreateCopy(
2114 : const char *pszFilename, GDALDataset *poSrcDS, CPL_UNUSED int bStrict,
2115 : CSLConstList papszOptions, GDALProgressFunc pfnProgress,
2116 : void *pProgressData)
2117 :
2118 : {
2119 282 : int nBands = poSrcDS->GetRasterCount();
2120 282 : int nXSize = poSrcDS->GetRasterXSize();
2121 282 : int nYSize = poSrcDS->GetRasterYSize();
2122 :
2123 282 : if (nBands == 0 || nBands > 16384)
2124 : {
2125 2 : CPLError(
2126 : CE_Failure, CPLE_NotSupported,
2127 : "Unable to export files with %d bands. Must be >= 1 and <= 16384",
2128 : nBands);
2129 2 : return nullptr;
2130 : }
2131 :
2132 280 : GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
2133 280 : if (poCT != nullptr && nBands != 1)
2134 : {
2135 1 : CPLError(CE_Failure, CPLE_NotSupported,
2136 : "JP2 driver only supports a color table for a "
2137 : "single-band dataset");
2138 1 : return nullptr;
2139 : }
2140 :
2141 279 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
2142 279 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
2143 279 : if (eDataType != GDT_UInt8 && eDataType != GDT_Int16 &&
2144 8 : eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
2145 : eDataType != GDT_UInt32)
2146 : {
2147 6 : CPLError(CE_Failure, CPLE_NotSupported,
2148 : "JP2 driver only supports creating Byte, GDT_Int16, "
2149 : "GDT_UInt16, GDT_Int32, GDT_UInt32");
2150 6 : return nullptr;
2151 : }
2152 :
2153 : /* -------------------------------------------------------------------- */
2154 : /* Transcode short-circuit (Grok only). */
2155 : /* -------------------------------------------------------------------- */
2156 273 : if (CPLFetchBool(papszOptions, "TRANSCODE", false))
2157 : {
2158 0 : if (!BASE::canPerformDirectIO())
2159 : {
2160 0 : CPLError(CE_Failure, CPLE_NotSupported,
2161 : "TRANSCODE=YES is only supported by the JP2Grok driver");
2162 0 : return nullptr;
2163 : }
2164 :
2165 0 : CPLString osSrcFilename(poSrcDS->GetDescription());
2166 0 : if (poSrcDS->GetDriver() != nullptr &&
2167 0 : poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
2168 : {
2169 0 : VRTDataset *poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
2170 0 : if (poVRTDS)
2171 : {
2172 : GDALDataset *poSimpleSourceDS =
2173 0 : poVRTDS->GetSingleSimpleSource();
2174 0 : if (poSimpleSourceDS)
2175 0 : osSrcFilename = poSimpleSourceDS->GetDescription();
2176 : }
2177 : }
2178 :
2179 0 : if (!CODEC::transcode(osSrcFilename, pszFilename, poSrcDS,
2180 : papszOptions))
2181 0 : return nullptr;
2182 :
2183 0 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
2184 0 : return Open(&oOpenInfo);
2185 : }
2186 :
2187 273 : const bool bInspireTG = CPLFetchBool(papszOptions, "INSPIRE_TG", false);
2188 :
2189 : /* -------------------------------------------------------------------- */
2190 : /* Analyze creation options. */
2191 : /* -------------------------------------------------------------------- */
2192 273 : auto eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
2193 273 : const char *pszCodec = CSLFetchNameValueDef(papszOptions, "CODEC", nullptr);
2194 273 : if (pszCodec)
2195 : {
2196 14 : if (EQUAL(pszCodec, "JP2"))
2197 5 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
2198 9 : else if (EQUAL(pszCodec, "J2K"))
2199 9 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
2200 : else
2201 : {
2202 0 : CPLError(CE_Warning, CPLE_NotSupported,
2203 : "Unsupported value for CODEC : %s. Defaulting to J2K",
2204 : pszCodec);
2205 : }
2206 : }
2207 : else
2208 : {
2209 259 : if (strlen(pszFilename) > 4 &&
2210 259 : EQUAL(pszFilename + strlen(pszFilename) - 4, ".JP2"))
2211 : {
2212 224 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
2213 : }
2214 : }
2215 273 : if (eCodecFormat != CODEC::cvtenum(JP2_CODEC_JP2) && bInspireTG)
2216 : {
2217 1 : CPLError(CE_Warning, CPLE_NotSupported,
2218 : "INSPIRE_TG=YES mandates CODEC=JP2 (TG requirement 21)");
2219 1 : return nullptr;
2220 : }
2221 :
2222 : // NOTE: if changing the default block size, the logic in nitfdataset.cpp
2223 : // CreateCopy() will have to be changed as well.
2224 272 : int nBlockXSize =
2225 272 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "1024"));
2226 272 : int nBlockYSize =
2227 272 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "1024"));
2228 272 : if (nBlockXSize <= 0 || nBlockYSize <= 0)
2229 : {
2230 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
2231 0 : return nullptr;
2232 : }
2233 :
2234 : // By default do not generate tile sizes larger than the dataset
2235 : // dimensions
2236 544 : if (!CPLFetchBool(papszOptions, "BLOCKSIZE_STRICT", false) &&
2237 272 : !CPLFetchBool(papszOptions, "@BLOCKSIZE_STRICT", false))
2238 : {
2239 268 : if (nBlockXSize < 32 || nBlockYSize < 32)
2240 : {
2241 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
2242 0 : return nullptr;
2243 : }
2244 :
2245 268 : if (nXSize < nBlockXSize)
2246 : {
2247 243 : CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
2248 : nBlockXSize, nXSize);
2249 243 : nBlockXSize = nXSize;
2250 : }
2251 268 : if (nYSize < nBlockYSize)
2252 : {
2253 244 : CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
2254 : nBlockYSize, nYSize);
2255 244 : nBlockYSize = nYSize;
2256 : }
2257 : }
2258 :
2259 272 : JP2_PROG_ORDER eProgOrder = JP2_LRCP;
2260 : const char *pszPROGORDER =
2261 272 : CSLFetchNameValueDef(papszOptions, "PROGRESSION", "LRCP");
2262 272 : if (EQUAL(pszPROGORDER, "LRCP"))
2263 272 : eProgOrder = JP2_LRCP;
2264 0 : else if (EQUAL(pszPROGORDER, "RLCP"))
2265 0 : eProgOrder = JP2_RLCP;
2266 0 : else if (EQUAL(pszPROGORDER, "RPCL"))
2267 0 : eProgOrder = JP2_RPCL;
2268 0 : else if (EQUAL(pszPROGORDER, "PCRL"))
2269 0 : eProgOrder = JP2_PCRL;
2270 0 : else if (EQUAL(pszPROGORDER, "CPRL"))
2271 0 : eProgOrder = JP2_CPRL;
2272 : else
2273 : {
2274 0 : CPLError(CE_Warning, CPLE_NotSupported,
2275 : "Unsupported value for PROGRESSION : %s. Defaulting to LRCP",
2276 : pszPROGORDER);
2277 : }
2278 :
2279 272 : const bool bIsIrreversible =
2280 272 : !CPLFetchBool(papszOptions, "REVERSIBLE", poCT != nullptr);
2281 :
2282 544 : std::vector<double> adfRates;
2283 : const char *pszQuality =
2284 272 : CSLFetchNameValueDef(papszOptions, "QUALITY", nullptr);
2285 272 : double dfDefaultQuality = (poCT != nullptr) ? 100.0 : 25.0;
2286 272 : if (pszQuality)
2287 : {
2288 : char **papszTokens =
2289 41 : CSLTokenizeStringComplex(pszQuality, ",", FALSE, FALSE);
2290 158 : for (int i = 0; papszTokens[i] != nullptr; i++)
2291 : {
2292 117 : double dfQuality = CPLAtof(papszTokens[i]);
2293 117 : if (dfQuality > 0 && dfQuality <= 100)
2294 : {
2295 117 : double dfRate = 100 / dfQuality;
2296 117 : adfRates.push_back(dfRate);
2297 : }
2298 : else
2299 : {
2300 0 : CPLError(CE_Warning, CPLE_NotSupported,
2301 : "Unsupported value for QUALITY: %s. Defaulting to "
2302 : "single-layer, with quality=%.0f",
2303 0 : papszTokens[i], dfDefaultQuality);
2304 0 : adfRates.resize(0);
2305 0 : break;
2306 : }
2307 : }
2308 41 : if (papszTokens[0] == nullptr)
2309 : {
2310 0 : CPLError(CE_Warning, CPLE_NotSupported,
2311 : "Unsupported value for QUALITY: %s. Defaulting to "
2312 : "single-layer, with quality=%.0f",
2313 : pszQuality, dfDefaultQuality);
2314 : }
2315 41 : CSLDestroy(papszTokens);
2316 : }
2317 272 : if (adfRates.empty())
2318 : {
2319 231 : adfRates.push_back(100. / dfDefaultQuality);
2320 231 : assert(!adfRates.empty());
2321 : }
2322 :
2323 272 : if (poCT != nullptr && (bIsIrreversible || adfRates.back() != 1.0))
2324 : {
2325 2 : CPLError(CE_Warning, CPLE_AppDefined,
2326 : "Encoding a dataset with a color table with REVERSIBLE != YES "
2327 : "or QUALITY != 100 will likely lead to bad visual results");
2328 : }
2329 :
2330 272 : const int nMaxTileDim = std::max(nBlockXSize, nBlockYSize);
2331 272 : const int nMinTileDim = std::min(nBlockXSize, nBlockYSize);
2332 272 : int nNumResolutions = 1;
2333 : /* Pickup a reasonable value compatible with PROFILE_1 requirements */
2334 363 : while ((nMaxTileDim >> (nNumResolutions - 1)) > 128 &&
2335 92 : (nMinTileDim >> nNumResolutions) > 0)
2336 91 : nNumResolutions++;
2337 272 : int nMinProfile1Resolutions = nNumResolutions;
2338 : const char *pszResolutions =
2339 272 : CSLFetchNameValueDef(papszOptions, "RESOLUTIONS", nullptr);
2340 272 : if (pszResolutions)
2341 : {
2342 10 : nNumResolutions = atoi(pszResolutions);
2343 10 : if (nNumResolutions <= 0 || nNumResolutions >= 32 ||
2344 9 : (nMinTileDim >> nNumResolutions) == 0 ||
2345 9 : (nMaxTileDim >> nNumResolutions) == 0)
2346 : {
2347 1 : CPLError(CE_Warning, CPLE_NotSupported,
2348 : "Unsupported value for RESOLUTIONS : %s. Defaulting to %d",
2349 : pszResolutions, nMinProfile1Resolutions);
2350 1 : nNumResolutions = nMinProfile1Resolutions;
2351 : }
2352 : }
2353 272 : int nRedBandIndex = -1;
2354 272 : int nGreenBandIndex = -1;
2355 272 : int nBlueBandIndex = -1;
2356 272 : int nAlphaBandIndex = -1;
2357 611 : for (int i = 0; i < nBands; i++)
2358 : {
2359 : GDALColorInterp eInterp =
2360 339 : poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation();
2361 339 : if (eInterp == GCI_RedBand)
2362 15 : nRedBandIndex = i;
2363 324 : else if (eInterp == GCI_GreenBand)
2364 15 : nGreenBandIndex = i;
2365 309 : else if (eInterp == GCI_BlueBand)
2366 15 : nBlueBandIndex = i;
2367 294 : else if (eInterp == GCI_AlphaBand)
2368 7 : nAlphaBandIndex = i;
2369 : }
2370 272 : const char *pszAlpha = CSLFetchNameValue(papszOptions, "ALPHA");
2371 275 : if (nAlphaBandIndex < 0 && nBands > 1 && pszAlpha != nullptr &&
2372 3 : CPLTestBool(pszAlpha))
2373 : {
2374 3 : nAlphaBandIndex = nBands - 1;
2375 : }
2376 :
2377 272 : const char *pszYCBCR420 = CSLFetchNameValue(papszOptions, "YCBCR420");
2378 272 : int bYCBCR420 = FALSE;
2379 272 : if (pszYCBCR420 && CPLTestBool(pszYCBCR420))
2380 : {
2381 2 : if ((nBands == 3 || nBands == 4) && eDataType == GDT_UInt8 &&
2382 2 : nRedBandIndex == 0 && nGreenBandIndex == 1 && nBlueBandIndex == 2)
2383 : {
2384 2 : if (((nXSize % 2) == 0 && (nYSize % 2) == 0 &&
2385 2 : (nBlockXSize % 2) == 0 && (nBlockYSize % 2) == 0))
2386 : {
2387 2 : bYCBCR420 = TRUE;
2388 : }
2389 : else
2390 : {
2391 0 : CPLError(CE_Warning, CPLE_NotSupported,
2392 : "YCBCR420 unsupported when image size and/or tile "
2393 : "size are not multiple of 2");
2394 : }
2395 : }
2396 : else
2397 : {
2398 0 : CPLError(CE_Warning, CPLE_NotSupported,
2399 : "YCBCR420 unsupported with this image band count and/or "
2400 : "data byte");
2401 : }
2402 : }
2403 :
2404 272 : const char *pszYCC = CSLFetchNameValue(papszOptions, "YCC");
2405 294 : int bYCC = ((nBands == 3 || nBands == 4) &&
2406 22 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "YCC", "TRUE")));
2407 :
2408 272 : if (bYCBCR420 && bYCC)
2409 : {
2410 2 : if (pszYCC != nullptr)
2411 : {
2412 0 : CPLError(CE_Warning, CPLE_NotSupported,
2413 : "YCC unsupported when YCbCr requesting");
2414 : }
2415 2 : bYCC = FALSE;
2416 : }
2417 :
2418 : /* -------------------------------------------------------------------- */
2419 : /* Deal with codeblocks size */
2420 : /* -------------------------------------------------------------------- */
2421 :
2422 : int nCblockW =
2423 272 : atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_WIDTH", "64"));
2424 : int nCblockH =
2425 272 : atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_HEIGHT", "64"));
2426 272 : if (nCblockW < 4 || nCblockW > 1024 || nCblockH < 4 || nCblockH > 1024)
2427 : {
2428 4 : CPLError(CE_Warning, CPLE_NotSupported,
2429 : "Invalid values for codeblock size. Defaulting to 64x64");
2430 4 : nCblockW = 64;
2431 4 : nCblockH = 64;
2432 : }
2433 268 : else if (nCblockW * nCblockH > 4096)
2434 : {
2435 1 : CPLError(CE_Warning, CPLE_NotSupported,
2436 : "Invalid values for codeblock size. "
2437 : "CODEBLOCK_WIDTH * CODEBLOCK_HEIGHT should be <= 4096. "
2438 : "Defaulting to 64x64");
2439 1 : nCblockW = 64;
2440 1 : nCblockH = 64;
2441 : }
2442 272 : int nCblockW_po2 = FloorPowerOfTwo(nCblockW);
2443 272 : int nCblockH_po2 = FloorPowerOfTwo(nCblockH);
2444 272 : if (nCblockW_po2 != nCblockW || nCblockH_po2 != nCblockH)
2445 : {
2446 1 : CPLError(CE_Warning, CPLE_NotSupported,
2447 : "Non power of two values used for codeblock size. "
2448 : "Using to %dx%d",
2449 : nCblockW_po2, nCblockH_po2);
2450 : }
2451 272 : nCblockW = nCblockW_po2;
2452 272 : nCblockH = nCblockH_po2;
2453 :
2454 : /* -------------------------------------------------------------------- */
2455 : /* Deal with codestream PROFILE */
2456 : /* -------------------------------------------------------------------- */
2457 : const char *pszProfile =
2458 272 : CSLFetchNameValueDef(papszOptions, "PROFILE", "AUTO");
2459 272 : int bProfile1 = FALSE;
2460 272 : if (EQUAL(pszProfile, "UNRESTRICTED"))
2461 : {
2462 1 : bProfile1 = FALSE;
2463 1 : if (bInspireTG)
2464 : {
2465 1 : CPLError(CE_Failure, CPLE_NotSupported,
2466 : "INSPIRE_TG=YES mandates PROFILE=PROFILE_1 (TG "
2467 : "requirement 21)");
2468 1 : return nullptr;
2469 : }
2470 : }
2471 271 : else if (EQUAL(pszProfile, "UNRESTRICTED_FORCED"))
2472 : {
2473 0 : bProfile1 = FALSE;
2474 : }
2475 271 : else if (EQUAL(pszProfile,
2476 : "PROFILE_1_FORCED")) /* For debug only: can produce
2477 : inconsistent codestream */
2478 : {
2479 0 : bProfile1 = TRUE;
2480 : }
2481 : else
2482 : {
2483 271 : if (!(EQUAL(pszProfile, "PROFILE_1") || EQUAL(pszProfile, "AUTO")))
2484 : {
2485 0 : CPLError(CE_Warning, CPLE_NotSupported,
2486 : "Unsupported value for PROFILE : %s. Defaulting to AUTO",
2487 : pszProfile);
2488 0 : pszProfile = "AUTO";
2489 : }
2490 :
2491 271 : bProfile1 = TRUE;
2492 271 : const char *pszReq21OrEmpty = bInspireTG ? " (TG requirement 21)" : "";
2493 271 : if ((nBlockXSize != nXSize || nBlockYSize != nYSize) &&
2494 24 : (nBlockXSize != nBlockYSize || nBlockXSize > 1024 ||
2495 19 : nBlockYSize > 1024))
2496 : {
2497 5 : bProfile1 = FALSE;
2498 5 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2499 : {
2500 2 : CPLError(
2501 : CE_Failure, CPLE_NotSupported,
2502 : "Tile dimensions incompatible with PROFILE_1%s. "
2503 : "Should be whole image or square with dimension <= 1024.",
2504 : pszReq21OrEmpty);
2505 2 : return nullptr;
2506 : }
2507 : }
2508 269 : if ((nMaxTileDim >> (nNumResolutions - 1)) > 128)
2509 : {
2510 4 : bProfile1 = FALSE;
2511 4 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2512 : {
2513 1 : CPLError(CE_Failure, CPLE_NotSupported,
2514 : "Number of resolutions incompatible with PROFILE_1%s. "
2515 : "Should be at least %d.",
2516 : pszReq21OrEmpty, nMinProfile1Resolutions);
2517 1 : return nullptr;
2518 : }
2519 : }
2520 268 : if (nCblockW > 64 || nCblockH > 64)
2521 : {
2522 2 : bProfile1 = FALSE;
2523 2 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2524 : {
2525 2 : CPLError(CE_Failure, CPLE_NotSupported,
2526 : "Codeblock width incompatible with PROFILE_1%s. "
2527 : "Codeblock width or height should be <= 64.",
2528 : pszReq21OrEmpty);
2529 2 : return nullptr;
2530 : }
2531 : }
2532 : }
2533 :
2534 : /* -------------------------------------------------------------------- */
2535 : /* Work out the precision. */
2536 : /* -------------------------------------------------------------------- */
2537 : int nBits;
2538 266 : const int nDTBits = GDALGetDataTypeSizeBits(eDataType);
2539 :
2540 266 : if (CSLFetchNameValue(papszOptions, "NBITS") != nullptr)
2541 : {
2542 22 : nBits = atoi(CSLFetchNameValue(papszOptions, "NBITS"));
2543 22 : if (bInspireTG &&
2544 1 : !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32))
2545 : {
2546 1 : CPLError(CE_Failure, CPLE_NotSupported,
2547 : "INSPIRE_TG=YES mandates NBITS=1,8,16 or 32 (TG "
2548 : "requirement 24)");
2549 1 : return nullptr;
2550 : }
2551 : }
2552 244 : else if (poSrcDS->GetRasterBand(1)->GetMetadataItem(
2553 244 : "NBITS", "IMAGE_STRUCTURE") != nullptr)
2554 : {
2555 3 : nBits = atoi(poSrcDS->GetRasterBand(1)->GetMetadataItem(
2556 : "NBITS", "IMAGE_STRUCTURE"));
2557 3 : if (bInspireTG &&
2558 1 : !(nBits == 1 || nBits == 8 || nBits == 16 || nBits == 32))
2559 : {
2560 : /* Implements "NOTE If the original data do not satisfy this "
2561 : "requirement, they will be converted in a representation using "
2562 : "the next higher power of 2" */
2563 1 : nBits = nDTBits;
2564 : }
2565 : }
2566 : else
2567 : {
2568 241 : nBits = nDTBits;
2569 : }
2570 :
2571 265 : if ((nDTBits == 8 && nBits > 8) ||
2572 265 : (nDTBits == 16 && (nBits <= 8 || nBits > 16)) ||
2573 2 : (nDTBits == 32 && (nBits <= 16 || nBits > 32)))
2574 : {
2575 0 : CPLError(CE_Warning, CPLE_NotSupported,
2576 : "Inconsistent NBITS value with data type. Using %d", nDTBits);
2577 : }
2578 :
2579 : /* -------------------------------------------------------------------- */
2580 : /* Georeferencing options */
2581 : /* -------------------------------------------------------------------- */
2582 :
2583 265 : bool bGMLJP2Option = CPLFetchBool(papszOptions, "GMLJP2", true);
2584 265 : int nGMLJP2Version = 1;
2585 : const char *pszGMLJP2V2Def =
2586 265 : CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
2587 265 : if (pszGMLJP2V2Def != nullptr)
2588 : {
2589 28 : bGMLJP2Option = true;
2590 28 : nGMLJP2Version = 2;
2591 28 : if (bInspireTG)
2592 : {
2593 0 : CPLError(CE_Warning, CPLE_NotSupported,
2594 : "INSPIRE_TG=YES is only compatible with GMLJP2 v1");
2595 0 : return nullptr;
2596 : }
2597 : }
2598 265 : const bool bGeoJP2Option = CPLFetchBool(papszOptions, "GeoJP2", true);
2599 :
2600 530 : GDALJP2Metadata oJP2MD;
2601 :
2602 265 : int bGeoreferencingCompatOfGeoJP2 = FALSE;
2603 265 : int bGeoreferencingCompatOfGMLJP2 = FALSE;
2604 271 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
2605 6 : (bGMLJP2Option || bGeoJP2Option))
2606 : {
2607 220 : if (poSrcDS->GetGCPCount() > 0)
2608 : {
2609 3 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2610 3 : oJP2MD.SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs());
2611 3 : oJP2MD.SetSpatialRef(poSrcDS->GetGCPSpatialRef());
2612 : }
2613 : else
2614 : {
2615 217 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
2616 217 : if (poSRS)
2617 : {
2618 57 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2619 57 : oJP2MD.SetSpatialRef(poSRS);
2620 : }
2621 217 : GDALGeoTransform gt;
2622 217 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
2623 : {
2624 164 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2625 164 : oJP2MD.SetGeoTransform(gt);
2626 164 : if (poSRS && !poSRS->IsEmpty())
2627 : {
2628 57 : bGeoreferencingCompatOfGMLJP2 =
2629 57 : GDALJP2Metadata::IsSRSCompatible(poSRS);
2630 57 : if (!bGeoreferencingCompatOfGMLJP2)
2631 : {
2632 1 : CPLDebug(
2633 : CODEC::debugId(),
2634 : "Cannot write GMLJP2 box due to unsupported SRS");
2635 : }
2636 : }
2637 : }
2638 : }
2639 220 : if (poSrcDS->GetMetadata("RPC") != nullptr)
2640 : {
2641 1 : oJP2MD.SetRPCMD(poSrcDS->GetMetadata("RPC"));
2642 1 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2643 : }
2644 :
2645 : const char *pszAreaOrPoint =
2646 220 : poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
2647 261 : oJP2MD.bPixelIsPoint = pszAreaOrPoint != nullptr &&
2648 41 : EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
2649 :
2650 436 : if (bGMLJP2Option &&
2651 216 : CPLGetConfigOption("GMLJP2OVERRIDE", nullptr) != nullptr)
2652 : {
2653 : // Force V1 since this is the branch in which the hack is
2654 : // implemented
2655 7 : nGMLJP2Version = 1;
2656 7 : bGeoreferencingCompatOfGMLJP2 = TRUE;
2657 : }
2658 : }
2659 :
2660 265 : if (CSLFetchNameValue(papszOptions, "GMLJP2") != nullptr && bGMLJP2Option &&
2661 : !bGeoreferencingCompatOfGMLJP2)
2662 : {
2663 0 : CPLError(CE_Warning, CPLE_AppDefined,
2664 : "GMLJP2 box was explicitly required but cannot be written due "
2665 : "to lack of georeferencing and/or unsupported georeferencing "
2666 : "for GMLJP2");
2667 : }
2668 :
2669 265 : if (CSLFetchNameValue(papszOptions, "GeoJP2") != nullptr && bGeoJP2Option &&
2670 : !bGeoreferencingCompatOfGeoJP2)
2671 : {
2672 0 : CPLError(CE_Warning, CPLE_AppDefined,
2673 : "GeoJP2 box was explicitly required but cannot be written due "
2674 : "to lack of georeferencing");
2675 : }
2676 : const bool bGeoBoxesAfter =
2677 265 : CPLFetchBool(papszOptions, "GEOBOXES_AFTER_JP2C", bInspireTG);
2678 265 : GDALJP2Box *poGMLJP2Box = nullptr;
2679 265 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) && bGMLJP2Option &&
2680 : bGeoreferencingCompatOfGMLJP2)
2681 : {
2682 61 : if (nGMLJP2Version == 1)
2683 33 : poGMLJP2Box = oJP2MD.CreateGMLJP2(nXSize, nYSize);
2684 : else
2685 : poGMLJP2Box =
2686 28 : oJP2MD.CreateGMLJP2V2(nXSize, nYSize, pszGMLJP2V2Def, poSrcDS);
2687 61 : if (poGMLJP2Box == nullptr)
2688 3 : return nullptr;
2689 : }
2690 :
2691 : /* ---------------------------------------------------------------- */
2692 : /* If the input driver is identified as "GEORASTER" the following */
2693 : /* section will try to dump a ORACLE GeoRaster JP2 BLOB into a file */
2694 : /* ---------------------------------------------------------------- */
2695 :
2696 262 : if (EQUAL(poSrcDS->GetDriverName(), "GEORASTER"))
2697 : {
2698 : const char *pszGEOR_compress =
2699 0 : poSrcDS->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
2700 :
2701 0 : if (pszGEOR_compress == nullptr)
2702 : {
2703 0 : pszGEOR_compress = "NONE";
2704 : }
2705 :
2706 : /* Check if the JP2 BLOB needs re-shaping */
2707 :
2708 0 : bool bGEOR_reshape = false;
2709 :
2710 0 : const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
2711 : "BLOCKYSIZE",
2712 : "QUALITY",
2713 : "REVERSIBLE",
2714 : "RESOLUTIONS",
2715 : "PROGRESSION",
2716 : "SOP",
2717 : "EPH",
2718 : "YCBCR420",
2719 : "YCC",
2720 : "NBITS",
2721 : "1BIT_ALPHA",
2722 : "PRECINCTS",
2723 : "TILEPARTS",
2724 : "CODEBLOCK_WIDTH",
2725 : "CODEBLOCK_HEIGHT",
2726 : "PLT",
2727 : "TLM",
2728 : nullptr};
2729 :
2730 0 : for (int i = 0; apszIgnoredOptions[i]; i++)
2731 : {
2732 0 : if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
2733 : {
2734 0 : bGEOR_reshape = true;
2735 : }
2736 : }
2737 :
2738 0 : if (CSLFetchNameValue(papszOptions, "USE_SRC_CODESTREAM"))
2739 : {
2740 0 : bGEOR_reshape = false;
2741 : }
2742 :
2743 0 : char **papszGEOR_files = poSrcDS->GetFileList();
2744 :
2745 0 : if (EQUAL(pszGEOR_compress, "JP2-F") && CSLCount(papszGEOR_files) > 0 &&
2746 0 : bGEOR_reshape == false)
2747 : {
2748 :
2749 0 : const char *pszVsiOciLob = papszGEOR_files[0];
2750 :
2751 0 : VSILFILE *fpBlob = VSIFOpenL(pszVsiOciLob, "r");
2752 0 : if (fpBlob == nullptr)
2753 : {
2754 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
2755 : pszVsiOciLob);
2756 0 : delete poGMLJP2Box;
2757 0 : return nullptr;
2758 : }
2759 0 : VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
2760 0 : if (fp == nullptr)
2761 : {
2762 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
2763 : pszFilename);
2764 0 : delete poGMLJP2Box;
2765 0 : VSIFCloseL(fpBlob);
2766 0 : return nullptr;
2767 : }
2768 :
2769 0 : VSIFSeekL(fpBlob, 0, SEEK_END);
2770 :
2771 0 : size_t nBlobSize = static_cast<size_t>(VSIFTellL(fpBlob));
2772 0 : size_t nChunk = GDALGetCacheMax() / 4;
2773 0 : size_t nSize = 0;
2774 0 : size_t nCount = 0;
2775 :
2776 0 : void *pBuffer = VSI_MALLOC_VERBOSE(nChunk);
2777 0 : if (pBuffer == nullptr)
2778 : {
2779 0 : delete poGMLJP2Box;
2780 0 : VSIFCloseL(fpBlob);
2781 0 : VSIFCloseL(fp);
2782 0 : return nullptr;
2783 : }
2784 :
2785 0 : VSIFSeekL(fpBlob, 0, SEEK_SET);
2786 :
2787 0 : while ((nSize = VSIFReadL(pBuffer, 1, nChunk, fpBlob)) > 0)
2788 : {
2789 0 : VSIFWriteL(pBuffer, 1, nSize, fp);
2790 0 : nCount += nSize;
2791 0 : pfnProgress(static_cast<double>(nCount) /
2792 : static_cast<double>(nBlobSize),
2793 : nullptr, pProgressData);
2794 : }
2795 :
2796 0 : CPLFree(pBuffer);
2797 0 : VSIFCloseL(fpBlob);
2798 :
2799 0 : VSIFCloseL(fp);
2800 :
2801 : /* Return the GDALDaset object */
2802 :
2803 0 : GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
2804 0 : GDALDataset *poDS = JP2OPJLikeDataset::Open(&oOpenInfo);
2805 :
2806 : /* Copy essential metadata */
2807 :
2808 0 : GDALGeoTransform gt;
2809 0 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
2810 : {
2811 0 : poDS->SetGeoTransform(gt);
2812 : }
2813 :
2814 0 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
2815 0 : if (poSRS)
2816 : {
2817 0 : poDS->SetSpatialRef(poSRS);
2818 : }
2819 :
2820 0 : delete poGMLJP2Box;
2821 0 : return poDS;
2822 : }
2823 : }
2824 :
2825 : /* -------------------------------------------------------------------- */
2826 : /* Setup encoder */
2827 : /* -------------------------------------------------------------------- */
2828 :
2829 524 : JP2OPJLikeDataset oTmpDS;
2830 262 : int numThreads = oTmpDS.GetNumThreads();
2831 :
2832 262 : CODEC localctx;
2833 262 : localctx.allocComponentParams(nBands);
2834 : int iBand;
2835 262 : int bSamePrecision = TRUE;
2836 262 : int b1BitAlpha = FALSE;
2837 591 : for (iBand = 0; iBand < nBands; iBand++)
2838 : {
2839 329 : localctx.pasBandParams[iBand].x0 = 0;
2840 329 : localctx.pasBandParams[iBand].y0 = 0;
2841 329 : if (bYCBCR420 && (iBand == 1 || iBand == 2))
2842 : {
2843 4 : localctx.pasBandParams[iBand].dx = 2;
2844 4 : localctx.pasBandParams[iBand].dy = 2;
2845 4 : localctx.pasBandParams[iBand].w = nXSize / 2;
2846 4 : localctx.pasBandParams[iBand].h = nYSize / 2;
2847 : }
2848 : else
2849 : {
2850 325 : localctx.pasBandParams[iBand].dx = 1;
2851 325 : localctx.pasBandParams[iBand].dy = 1;
2852 325 : localctx.pasBandParams[iBand].w = nXSize;
2853 325 : localctx.pasBandParams[iBand].h = nYSize;
2854 : }
2855 :
2856 329 : localctx.pasBandParams[iBand].sgnd =
2857 329 : (eDataType == GDT_Int16 || eDataType == GDT_Int32);
2858 329 : localctx.pasBandParams[iBand].prec = nBits;
2859 :
2860 : const char *pszNBits =
2861 329 : poSrcDS->GetRasterBand(iBand + 1)->GetMetadataItem(
2862 : "NBITS", "IMAGE_STRUCTURE");
2863 : /* Recommendation 38 In the case of an opacity channel, the bit depth
2864 : * should be 1-bit. */
2865 339 : if (iBand == nAlphaBandIndex &&
2866 0 : ((pszNBits != nullptr && EQUAL(pszNBits, "1")) ||
2867 10 : CPLFetchBool(papszOptions, "1BIT_ALPHA", bInspireTG)))
2868 : {
2869 3 : if (iBand != nBands - 1 && nBits != 1)
2870 : {
2871 : /* Might be a bug in openjpeg, but it seems that if the alpha */
2872 : /* band is the first one, it would select 1-bit for all
2873 : * channels... */
2874 0 : CPLError(CE_Warning, CPLE_NotSupported,
2875 : "Cannot output 1-bit alpha channel if it is not the "
2876 : "last one");
2877 : }
2878 : else
2879 : {
2880 3 : CPLDebug(CODEC::debugId(), "Using 1-bit alpha channel");
2881 3 : localctx.pasBandParams[iBand].sgnd = 0;
2882 3 : localctx.pasBandParams[iBand].prec = 1;
2883 3 : bSamePrecision = FALSE;
2884 3 : b1BitAlpha = TRUE;
2885 : }
2886 : }
2887 : }
2888 :
2889 262 : if (bInspireTG && nAlphaBandIndex >= 0 && !b1BitAlpha)
2890 : {
2891 0 : CPLError(
2892 : CE_Warning, CPLE_NotSupported,
2893 : "INSPIRE_TG=YES recommends 1BIT_ALPHA=YES (Recommendation 38)");
2894 : }
2895 262 : auto eColorSpace = CODEC::cvtenum(JP2_CLRSPC_GRAY);
2896 :
2897 262 : if (bYCBCR420)
2898 : {
2899 2 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SYCC);
2900 : }
2901 260 : else if ((nBands == 3 || nBands == 4) && nRedBandIndex >= 0 &&
2902 13 : nGreenBandIndex >= 0 && nBlueBandIndex >= 0)
2903 : {
2904 13 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
2905 : }
2906 247 : else if (poCT != nullptr)
2907 : {
2908 6 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
2909 : }
2910 :
2911 : /* -------------------------------------------------------------------- */
2912 : /* Create the dataset. */
2913 : /* -------------------------------------------------------------------- */
2914 :
2915 262 : const char *pszAccess =
2916 262 : STARTS_WITH_CI(pszFilename, "/vsisubfile/") ? "r+b" : "w+b";
2917 524 : VSIVirtualHandleUniquePtr fpOwner(VSIFOpenL(pszFilename, pszAccess));
2918 262 : if (!fpOwner)
2919 : {
2920 3 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create file");
2921 3 : CPLFree(localctx.pasBandParams);
2922 3 : localctx.pasBandParams = nullptr;
2923 3 : delete poGMLJP2Box;
2924 3 : return nullptr;
2925 : }
2926 259 : VSILFILE *fp = fpOwner.get();
2927 :
2928 : /* -------------------------------------------------------------------- */
2929 : /* Add JP2 boxes. */
2930 : /* -------------------------------------------------------------------- */
2931 259 : vsi_l_offset nStartJP2C = 0;
2932 259 : int bUseXLBoxes = FALSE;
2933 :
2934 478 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
2935 219 : !BASE::canPerformDirectIO())
2936 : {
2937 438 : GDALJP2Box jPBox(fp);
2938 219 : jPBox.SetType("jP ");
2939 219 : jPBox.AppendWritableData(4, "\x0D\x0A\x87\x0A");
2940 219 : WriteBox(fp, &jPBox);
2941 :
2942 438 : GDALJP2Box ftypBox(fp);
2943 219 : ftypBox.SetType("ftyp");
2944 : // http://docs.opengeospatial.org/is/08-085r5/08-085r5.html Req 19
2945 219 : const bool bJPXOption = CPLFetchBool(papszOptions, "JPX", true);
2946 219 : if (nGMLJP2Version == 2 && bJPXOption)
2947 25 : ftypBox.AppendWritableData(4, "jpx "); /* Branding */
2948 : else
2949 194 : ftypBox.AppendWritableData(4, "jp2 "); /* Branding */
2950 219 : ftypBox.AppendUInt32(0); /* minimum version */
2951 219 : ftypBox.AppendWritableData(
2952 : 4, "jp2 "); /* Compatibility list: first value */
2953 :
2954 219 : if (bInspireTG && poGMLJP2Box != nullptr && !bJPXOption)
2955 : {
2956 1 : CPLError(
2957 : CE_Warning, CPLE_AppDefined,
2958 : "INSPIRE_TG=YES implies following GMLJP2 specification which "
2959 : "recommends advertise reader requirement 67 feature, and thus "
2960 : "JPX capability");
2961 : }
2962 218 : else if (poGMLJP2Box != nullptr && bJPXOption)
2963 : {
2964 : /* GMLJP2 uses lbl and asoc boxes, which are JPEG2000 Part II spec
2965 : */
2966 : /* advertizing jpx is required per 8.1 of 05-047r3 GMLJP2 */
2967 57 : ftypBox.AppendWritableData(
2968 : 4, "jpx "); /* Compatibility list: second value */
2969 : }
2970 219 : WriteBox(fp, &ftypBox);
2971 :
2972 221 : const bool bIPR = poSrcDS->GetMetadata("xml:IPR") != nullptr &&
2973 2 : CPLFetchBool(papszOptions, "WRITE_METADATA", false);
2974 :
2975 : /* Reader requirement box */
2976 219 : if (poGMLJP2Box != nullptr && bJPXOption)
2977 : {
2978 114 : GDALJP2Box rreqBox(fp);
2979 57 : rreqBox.SetType("rreq");
2980 57 : rreqBox.AppendUInt8(1); /* ML = 1 byte for mask length */
2981 :
2982 57 : rreqBox.AppendUInt8(0x80 | 0x40 | (bIPR ? 0x20 : 0)); /* FUAM */
2983 57 : rreqBox.AppendUInt8(0x80); /* DCM */
2984 :
2985 57 : rreqBox.AppendUInt16(
2986 : 2 + (bIPR ? 1 : 0)); /* NSF: Number of standard features */
2987 :
2988 57 : rreqBox.AppendUInt16(
2989 : (bProfile1) ? 4 : 5); /* SF0 : PROFILE 1 or PROFILE 2 */
2990 57 : rreqBox.AppendUInt8(0x80); /* SM0 */
2991 :
2992 57 : rreqBox.AppendUInt16(67); /* SF1 : GMLJP2 box */
2993 57 : rreqBox.AppendUInt8(0x40); /* SM1 */
2994 :
2995 57 : if (bIPR)
2996 : {
2997 0 : rreqBox.AppendUInt16(35); /* SF2 : IPR metadata */
2998 0 : rreqBox.AppendUInt8(0x20); /* SM2 */
2999 : }
3000 57 : rreqBox.AppendUInt16(0); /* NVF */
3001 57 : WriteBox(fp, &rreqBox);
3002 : }
3003 :
3004 438 : GDALJP2Box ihdrBox(fp);
3005 219 : ihdrBox.SetType("ihdr");
3006 219 : ihdrBox.AppendUInt32(nYSize);
3007 219 : ihdrBox.AppendUInt32(nXSize);
3008 219 : ihdrBox.AppendUInt16(static_cast<GUInt16>(nBands));
3009 : GByte BPC;
3010 219 : if (bSamePrecision)
3011 216 : BPC = static_cast<GByte>((localctx.pasBandParams[0].prec - 1) |
3012 216 : (localctx.pasBandParams[0].sgnd << 7));
3013 : else
3014 3 : BPC = 255;
3015 219 : ihdrBox.AppendUInt8(BPC);
3016 219 : ihdrBox.AppendUInt8(7); /* C=Compression type: fixed value */
3017 219 : ihdrBox.AppendUInt8(0); /* UnkC: 0= colourspace of the image is known */
3018 : /*and correctly specified in the Colourspace Specification boxes within
3019 : * the file */
3020 219 : ihdrBox.AppendUInt8(
3021 : bIPR ? 1 : 0); /* IPR: 0=no intellectual property, 1=IPR box */
3022 :
3023 438 : GDALJP2Box bpccBox(fp);
3024 219 : if (!bSamePrecision)
3025 : {
3026 3 : bpccBox.SetType("bpcc");
3027 13 : for (int i = 0; i < nBands; i++)
3028 10 : bpccBox.AppendUInt8(
3029 10 : static_cast<GByte>((localctx.pasBandParams[i].prec - 1) |
3030 10 : (localctx.pasBandParams[i].sgnd << 7)));
3031 : }
3032 :
3033 438 : GDALJP2Box colrBox(fp);
3034 219 : colrBox.SetType("colr");
3035 219 : colrBox.AppendUInt8(1); /* METHOD: 1=Enumerated Colourspace */
3036 219 : colrBox.AppendUInt8(
3037 : 0); /* PREC: Precedence. 0=(field reserved for ISO use) */
3038 219 : colrBox.AppendUInt8(0); /* APPROX: Colourspace approximation. */
3039 219 : GUInt32 enumcs = 16;
3040 219 : if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB))
3041 16 : enumcs = 16;
3042 203 : else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY))
3043 201 : enumcs = 17;
3044 2 : else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC))
3045 2 : enumcs = 18;
3046 219 : colrBox.AppendUInt32(enumcs); /* EnumCS: Enumerated colourspace */
3047 :
3048 438 : GDALJP2Box pclrBox(fp);
3049 438 : GDALJP2Box cmapBox(fp);
3050 219 : int nCTComponentCount = 0;
3051 219 : if (poCT != nullptr)
3052 : {
3053 6 : pclrBox.SetType("pclr");
3054 6 : const int nEntries = std::min(256, poCT->GetColorEntryCount());
3055 : nCTComponentCount =
3056 6 : atoi(CSLFetchNameValueDef(papszOptions, "CT_COMPONENTS", "0"));
3057 6 : if (bInspireTG)
3058 : {
3059 0 : if (nCTComponentCount != 0 && nCTComponentCount != 3)
3060 0 : CPLError(
3061 : CE_Warning, CPLE_AppDefined,
3062 : "Inspire TG mandates 3 components for color table");
3063 : else
3064 0 : nCTComponentCount = 3;
3065 : }
3066 6 : else if (nCTComponentCount != 3 && nCTComponentCount != 4)
3067 : {
3068 5 : nCTComponentCount = 3;
3069 21 : for (int i = 0; i < nEntries; i++)
3070 : {
3071 17 : const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
3072 17 : if (psEntry->c4 != 255)
3073 : {
3074 1 : CPLDebug(
3075 : CODEC::debugId(),
3076 : "Color table has at least one non-opaque value. "
3077 : "This may cause compatibility problems with some "
3078 : "readers. "
3079 : "In which case use CT_COMPONENTS=3 creation "
3080 : "option");
3081 1 : nCTComponentCount = 4;
3082 1 : break;
3083 : }
3084 : }
3085 : }
3086 6 : nRedBandIndex = 0;
3087 6 : nGreenBandIndex = 1;
3088 6 : nBlueBandIndex = 2;
3089 6 : nAlphaBandIndex = (nCTComponentCount == 4) ? 3 : -1;
3090 :
3091 6 : pclrBox.AppendUInt16(static_cast<GUInt16>(nEntries));
3092 6 : pclrBox.AppendUInt8(static_cast<GByte>(
3093 : nCTComponentCount)); /* NPC: Number of components */
3094 25 : for (int i = 0; i < nCTComponentCount; i++)
3095 : {
3096 19 : pclrBox.AppendUInt8(7); /* Bi: unsigned 8 bits */
3097 : }
3098 30 : for (int i = 0; i < nEntries; i++)
3099 : {
3100 24 : const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
3101 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c1));
3102 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c2));
3103 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c3));
3104 24 : if (nCTComponentCount == 4)
3105 4 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c4));
3106 : }
3107 :
3108 6 : cmapBox.SetType("cmap");
3109 25 : for (int i = 0; i < nCTComponentCount; i++)
3110 : {
3111 19 : cmapBox.AppendUInt16(0); /* CMPi: code stream component index */
3112 19 : cmapBox.AppendUInt8(1); /* MYTPi: 1=palette mapping */
3113 19 : cmapBox.AppendUInt8(static_cast<GByte>(
3114 : i)); /* PCOLi: index component from the map */
3115 : }
3116 : }
3117 :
3118 438 : GDALJP2Box cdefBox(fp);
3119 230 : if (((nBands == 3 || nBands == 4) &&
3120 24 : (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
3121 19 : eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
3122 10 : (nRedBandIndex != 0 || nGreenBandIndex != 1 ||
3123 438 : nBlueBandIndex != 2)) ||
3124 : nAlphaBandIndex >= 0)
3125 : {
3126 12 : cdefBox.SetType("cdef");
3127 12 : int nComponents = (nCTComponentCount == 4) ? 4 : nBands;
3128 12 : cdefBox.AppendUInt16(static_cast<GUInt16>(nComponents));
3129 55 : for (int i = 0; i < nComponents; i++)
3130 : {
3131 43 : uint16_t nTyp = 65535; // Unspecified
3132 43 : uint16_t nAsoc = 65535; // Unassociated
3133 43 : if (i != nAlphaBandIndex)
3134 : {
3135 32 : if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
3136 : i == 0)
3137 : {
3138 4 : nTyp =
3139 : 0; // colour image data for the associated colour
3140 4 : nAsoc = 1; // associated with a particular colour
3141 : }
3142 35 : else if ((eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
3143 56 : eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
3144 21 : (nComponents == 3 || nComponents == 4))
3145 : {
3146 24 : nTyp =
3147 : 0; // colour image data for the associated colour
3148 24 : if (i == nRedBandIndex)
3149 8 : nAsoc = 1;
3150 16 : else if (i == nGreenBandIndex)
3151 8 : nAsoc = 2;
3152 8 : else if (i == nBlueBandIndex)
3153 8 : nAsoc = 3;
3154 : else
3155 : {
3156 0 : CPLError(CE_Warning, CPLE_AppDefined,
3157 : "Could not associate band %d with a "
3158 : "red/green/blue channel",
3159 : i + 1);
3160 : }
3161 : }
3162 : }
3163 : else
3164 : {
3165 11 : nTyp = 1; // Non pre-multiplied alpha
3166 11 : nAsoc = 0; // Associated to the image as a whole
3167 : }
3168 :
3169 : // Component number
3170 43 : cdefBox.AppendUInt16(static_cast<GUInt16>(i));
3171 43 : cdefBox.AppendUInt16(nTyp);
3172 43 : cdefBox.AppendUInt16(nAsoc);
3173 : }
3174 : }
3175 :
3176 : // Add res box if needed
3177 219 : GDALJP2Box *poRes = nullptr;
3178 219 : if (poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION") != nullptr &&
3179 224 : poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION") != nullptr &&
3180 5 : poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT") != nullptr)
3181 : {
3182 : double dfXRes =
3183 5 : CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION"));
3184 : double dfYRes =
3185 5 : CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION"));
3186 : int nResUnit =
3187 5 : atoi(poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT"));
3188 : #define PIXELS_PER_INCH 2
3189 : #define PIXELS_PER_CM 3
3190 :
3191 5 : if (nResUnit == PIXELS_PER_INCH)
3192 : {
3193 : // convert pixels per inch to pixels per cm.
3194 2 : dfXRes = dfXRes * 39.37 / 100.0;
3195 2 : dfYRes = dfYRes * 39.37 / 100.0;
3196 2 : nResUnit = PIXELS_PER_CM;
3197 : }
3198 :
3199 5 : if (nResUnit == PIXELS_PER_CM && dfXRes > 0 && dfYRes > 0 &&
3200 5 : dfXRes < 65535 && dfYRes < 65535)
3201 : {
3202 : /* Format a resd box and embed it inside a res box */
3203 10 : GDALJP2Box oResd;
3204 5 : oResd.SetType("resd");
3205 :
3206 5 : int nYDenom = 1;
3207 58 : while (nYDenom < 32767 && dfYRes < 32767)
3208 : {
3209 53 : dfYRes *= 2;
3210 53 : nYDenom *= 2;
3211 : }
3212 5 : int nXDenom = 1;
3213 56 : while (nXDenom < 32767 && dfXRes < 32767)
3214 : {
3215 51 : dfXRes *= 2;
3216 51 : nXDenom *= 2;
3217 : }
3218 :
3219 5 : oResd.AppendUInt16(static_cast<GUInt16>(dfYRes));
3220 5 : oResd.AppendUInt16(static_cast<GUInt16>(nYDenom));
3221 5 : oResd.AppendUInt16(static_cast<GUInt16>(dfXRes));
3222 5 : oResd.AppendUInt16(static_cast<GUInt16>(nXDenom));
3223 5 : oResd.AppendUInt8(2); /* vertical exponent */
3224 5 : oResd.AppendUInt8(2); /* horizontal exponent */
3225 :
3226 5 : GDALJP2Box *poResd = &oResd;
3227 5 : poRes = GDALJP2Box::CreateAsocBox(1, &poResd);
3228 5 : poRes->SetType("res ");
3229 : }
3230 : }
3231 :
3232 : /* Build and write jp2h super box now */
3233 : GDALJP2Box *apoBoxes[7];
3234 219 : int nBoxes = 1;
3235 219 : apoBoxes[0] = &ihdrBox;
3236 219 : if (bpccBox.GetDataLength())
3237 3 : apoBoxes[nBoxes++] = &bpccBox;
3238 219 : apoBoxes[nBoxes++] = &colrBox;
3239 219 : if (pclrBox.GetDataLength())
3240 6 : apoBoxes[nBoxes++] = &pclrBox;
3241 219 : if (cmapBox.GetDataLength())
3242 6 : apoBoxes[nBoxes++] = &cmapBox;
3243 219 : if (cdefBox.GetDataLength())
3244 12 : apoBoxes[nBoxes++] = &cdefBox;
3245 219 : if (poRes)
3246 5 : apoBoxes[nBoxes++] = poRes;
3247 : GDALJP2Box *psJP2HBox =
3248 219 : GDALJP2Box::CreateSuperBox("jp2h", nBoxes, apoBoxes);
3249 219 : WriteBox(fp, psJP2HBox);
3250 219 : delete psJP2HBox;
3251 219 : delete poRes;
3252 :
3253 219 : if (!bGeoBoxesAfter)
3254 : {
3255 208 : if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
3256 : {
3257 150 : GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
3258 150 : WriteBox(fp, poBox);
3259 150 : delete poBox;
3260 : }
3261 :
3262 219 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
3263 11 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3264 : {
3265 11 : WriteXMPBox(fp, poSrcDS);
3266 : }
3267 :
3268 208 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3269 : {
3270 11 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3271 11 : WriteXMLBoxes(fp, poSrcDS);
3272 11 : WriteGDALMetadataBox(fp, poSrcDS, papszOptions);
3273 : }
3274 :
3275 208 : if (poGMLJP2Box != nullptr)
3276 : {
3277 53 : WriteBox(fp, poGMLJP2Box);
3278 : }
3279 : }
3280 : }
3281 :
3282 : /* -------------------------------------------------------------------- */
3283 : /* Try lossless reuse of an existing JPEG2000 codestream */
3284 : /* -------------------------------------------------------------------- */
3285 259 : vsi_l_offset nCodeStreamLength = 0;
3286 259 : vsi_l_offset nCodeStreamStart = 0;
3287 259 : VSILFILE *fpSrc = nullptr;
3288 259 : if (CPLFetchBool(papszOptions, "USE_SRC_CODESTREAM", false))
3289 : {
3290 14 : CPLString osSrcFilename(poSrcDS->GetDescription());
3291 14 : if (poSrcDS->GetDriver() != nullptr &&
3292 7 : poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
3293 : {
3294 0 : VRTDataset *poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
3295 0 : if (poVRTDS)
3296 : {
3297 : GDALDataset *poSimpleSourceDS =
3298 0 : poVRTDS->GetSingleSimpleSource();
3299 0 : if (poSimpleSourceDS)
3300 0 : osSrcFilename = poSimpleSourceDS->GetDescription();
3301 : }
3302 : }
3303 :
3304 7 : fpSrc = VSIFOpenL(osSrcFilename, "rb");
3305 7 : if (fpSrc)
3306 : {
3307 7 : nCodeStreamStart = JP2FindCodeStream(fpSrc, &nCodeStreamLength);
3308 : }
3309 7 : if (nCodeStreamLength == 0)
3310 : {
3311 1 : CPLError(
3312 : CE_Warning, CPLE_AppDefined,
3313 : "USE_SRC_CODESTREAM=YES specified, but no codestream found");
3314 : }
3315 : }
3316 :
3317 478 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
3318 219 : !BASE::canPerformDirectIO())
3319 : {
3320 : // Start codestream box
3321 219 : nStartJP2C = VSIFTellL(fp);
3322 219 : if (nCodeStreamLength)
3323 6 : bUseXLBoxes = nCodeStreamLength > UINT_MAX;
3324 : else
3325 426 : bUseXLBoxes = CPLFetchBool(papszOptions, "JP2C_XLBOX",
3326 425 : false) || /* For debugging */
3327 212 : static_cast<GIntBig>(nXSize) * nYSize * nBands *
3328 212 : nDataTypeSize / adfRates.back() >
3329 : 4e9;
3330 219 : GUInt32 nLBox = (bUseXLBoxes) ? 1 : 0;
3331 219 : CPL_MSBPTR32(&nLBox);
3332 219 : VSIFWriteL(&nLBox, 1, 4, fp);
3333 219 : VSIFWriteL("jp2c", 1, 4, fp);
3334 219 : if (bUseXLBoxes)
3335 : {
3336 1 : GUIntBig nXLBox = 0;
3337 1 : VSIFWriteL(&nXLBox, 1, 8, fp);
3338 : }
3339 : }
3340 :
3341 : /* -------------------------------------------------------------------- */
3342 : /* Do lossless reuse of an existing JPEG2000 codestream */
3343 : /* -------------------------------------------------------------------- */
3344 259 : if (fpSrc)
3345 : {
3346 7 : const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
3347 : "BLOCKYSIZE",
3348 : "QUALITY",
3349 : "REVERSIBLE",
3350 : "RESOLUTIONS",
3351 : "PROGRESSION",
3352 : "SOP",
3353 : "EPH",
3354 : "YCBCR420",
3355 : "YCC",
3356 : "NBITS",
3357 : "1BIT_ALPHA",
3358 : "PRECINCTS",
3359 : "TILEPARTS",
3360 : "CODEBLOCK_WIDTH",
3361 : "CODEBLOCK_HEIGHT",
3362 : "PLT",
3363 : nullptr};
3364 126 : for (int i = 0; apszIgnoredOptions[i]; i++)
3365 : {
3366 119 : if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
3367 : {
3368 1 : CPLError(CE_Warning, CPLE_NotSupported,
3369 : "Option %s ignored when USE_SRC_CODESTREAM=YES",
3370 : apszIgnoredOptions[i]);
3371 : }
3372 : }
3373 : GByte abyBuffer[4096];
3374 7 : VSIFSeekL(fpSrc, nCodeStreamStart, SEEK_SET);
3375 7 : vsi_l_offset nRead = 0;
3376 : /* coverity[tainted_data] */
3377 17 : while (nRead < nCodeStreamLength)
3378 : {
3379 10 : const size_t nToRead =
3380 10 : (nCodeStreamLength - nRead > 4096)
3381 : ? 4096
3382 : : static_cast<size_t>(nCodeStreamLength - nRead);
3383 10 : if (VSIFReadL(abyBuffer, 1, nToRead, fpSrc) != nToRead)
3384 : {
3385 0 : VSIFCloseL(fpSrc);
3386 0 : delete poGMLJP2Box;
3387 0 : return nullptr;
3388 : }
3389 10 : if (nRead == 0 && (pszProfile || bInspireTG) &&
3390 6 : abyBuffer[2] == 0xFF && abyBuffer[3] == 0x51)
3391 : {
3392 6 : if (EQUAL(pszProfile, "UNRESTRICTED"))
3393 : {
3394 0 : abyBuffer[6] = 0;
3395 0 : abyBuffer[7] = 0;
3396 : }
3397 6 : else if (EQUAL(pszProfile, "PROFILE_1") || bInspireTG)
3398 : {
3399 : // TODO: ultimately we should check that we can really set
3400 : // Profile 1
3401 1 : abyBuffer[6] = 0;
3402 1 : abyBuffer[7] = 2;
3403 : }
3404 : }
3405 20 : if (VSIFWriteL(abyBuffer, 1, nToRead, fp) != nToRead ||
3406 10 : !pfnProgress((nRead + nToRead) * 1.0 / nCodeStreamLength,
3407 : nullptr, pProgressData))
3408 : {
3409 0 : VSIFCloseL(fpSrc);
3410 0 : delete poGMLJP2Box;
3411 0 : return nullptr;
3412 : }
3413 10 : nRead += nToRead;
3414 : }
3415 :
3416 7 : VSIFCloseL(fpSrc);
3417 : }
3418 : else
3419 : {
3420 252 : localctx.open(fp);
3421 252 : if (!localctx.initCompress(papszOptions, adfRates, nBlockXSize,
3422 : nBlockYSize, bIsIrreversible,
3423 : nNumResolutions, eProgOrder, bYCC, nCblockW,
3424 : nCblockH, bYCBCR420, bProfile1, nBands,
3425 : nXSize, nYSize, eColorSpace, numThreads))
3426 : {
3427 0 : CPLError(CE_Failure, CPLE_AppDefined, "init compress failed");
3428 0 : localctx.free();
3429 0 : delete poGMLJP2Box;
3430 11 : return nullptr;
3431 : }
3432 :
3433 : /* Grok JP2: let codec handle box writing natively */
3434 252 : if (BASE::canPerformDirectIO() &&
3435 0 : eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
3436 : {
3437 0 : localctx.setupJP2Metadata(
3438 : bInspireTG, bProfile1, bGeoBoxesAfter,
3439 0 : (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2) ? &oJP2MD
3440 : : nullptr,
3441 : poGMLJP2Box, nAlphaBandIndex, nRedBandIndex, nGreenBandIndex,
3442 : nBlueBandIndex, eColorSpace, nBands, poCT, poSrcDS,
3443 : papszOptions);
3444 : }
3445 :
3446 252 : if (!localctx.initCodec(pszFilename, fpOwner))
3447 : {
3448 0 : CPLError(CE_Failure, CPLE_AppDefined,
3449 : "codec initialization failed");
3450 0 : localctx.free();
3451 0 : delete poGMLJP2Box;
3452 0 : return nullptr;
3453 : }
3454 252 : if (!fpOwner)
3455 0 : fp = nullptr;
3456 :
3457 252 : const int nTilesX = DIV_ROUND_UP(nXSize, nBlockXSize);
3458 252 : const int nTilesY = DIV_ROUND_UP(nYSize, nBlockYSize);
3459 :
3460 252 : const GUIntBig nTileSize = static_cast<GUIntBig>(nBlockXSize) *
3461 252 : nBlockYSize * nBands * nDataTypeSize;
3462 252 : GByte *pTempBuffer = nullptr;
3463 :
3464 252 : const bool bUseIOThread =
3465 504 : CODEC::preferPerTileCompress() && (nTilesX > 1 || nTilesY > 1) &&
3466 11 : nTileSize < 10 * 1024 * 1024 &&
3467 515 : strcmp(CPLGetThreadingModel(), "stub") != 0 &&
3468 11 : CPLTestBool(
3469 : CPLGetConfigOption("JP2OPENJPEG_USE_THREADED_IO", "YES"));
3470 :
3471 252 : if (nTileSize > UINT_MAX)
3472 : {
3473 1 : CPLError(CE_Failure, CPLE_NotSupported, "Tile size exceeds 4GB");
3474 1 : pTempBuffer = nullptr;
3475 : }
3476 : else
3477 : {
3478 : // Double memory buffer when using threaded I/O
3479 251 : const size_t nBufferSize =
3480 : static_cast<size_t>(bUseIOThread ? nTileSize * 2 : nTileSize);
3481 251 : pTempBuffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBufferSize));
3482 : }
3483 252 : if (pTempBuffer == nullptr)
3484 : {
3485 1 : localctx.free();
3486 1 : delete poGMLJP2Box;
3487 1 : return nullptr;
3488 : }
3489 :
3490 251 : GByte *pYUV420Buffer = nullptr;
3491 251 : if (bYCBCR420)
3492 : {
3493 2 : pYUV420Buffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(
3494 : nBlockXSize * nBlockYSize + nBlockXSize * nBlockYSize / 2 +
3495 : ((nBands == 4) ? nBlockXSize * nBlockYSize : 0)));
3496 2 : if (pYUV420Buffer == nullptr)
3497 : {
3498 0 : localctx.free();
3499 0 : CPLFree(pTempBuffer);
3500 0 : delete poGMLJP2Box;
3501 0 : return nullptr;
3502 : }
3503 : }
3504 :
3505 : /* --------------------------------------------------------------------
3506 : */
3507 : /* Iterate over the tiles */
3508 : /* --------------------------------------------------------------------
3509 : */
3510 251 : pfnProgress(0.0, nullptr, pProgressData);
3511 :
3512 : struct ReadRasterJob
3513 : {
3514 : GDALDataset *poSrcDS;
3515 : int nXOff;
3516 : int nYOff;
3517 : int nWidthToRead;
3518 : int nHeightToRead;
3519 : GDALDataType eDataType;
3520 : GByte *pBuffer;
3521 : int nBands;
3522 : CPLErr eErr;
3523 : };
3524 :
3525 814 : const auto ReadRasterFunction = [](void *threadData)
3526 : {
3527 407 : ReadRasterJob *job = static_cast<ReadRasterJob *>(threadData);
3528 814 : job->eErr = job->poSrcDS->RasterIO(
3529 : GF_Read, job->nXOff, job->nYOff, job->nWidthToRead,
3530 407 : job->nHeightToRead, job->pBuffer, job->nWidthToRead,
3531 : job->nHeightToRead, job->eDataType, job->nBands, nullptr, 0, 0,
3532 : 0, nullptr);
3533 : };
3534 :
3535 251 : CPLWorkerThreadPool oPool;
3536 251 : if (bUseIOThread)
3537 : {
3538 10 : oPool.Setup(1, nullptr, nullptr);
3539 : }
3540 :
3541 251 : GByte *pabyActiveBuffer = pTempBuffer;
3542 251 : GByte *pabyBackgroundBuffer =
3543 251 : pTempBuffer + static_cast<size_t>(nTileSize);
3544 :
3545 251 : CPLErr eErr = CE_None;
3546 251 : int iTile = 0;
3547 :
3548 : ReadRasterJob job;
3549 251 : job.eDataType = eDataType;
3550 251 : job.pBuffer = pabyActiveBuffer;
3551 251 : job.nBands = nBands;
3552 251 : job.eErr = CE_Failure;
3553 251 : job.poSrcDS = poSrcDS;
3554 :
3555 251 : if (bUseIOThread)
3556 : {
3557 10 : job.nXOff = 0;
3558 10 : job.nYOff = 0;
3559 10 : job.nWidthToRead = std::min(nBlockXSize, nXSize);
3560 10 : job.nHeightToRead = std::min(nBlockYSize, nYSize);
3561 10 : job.pBuffer = pabyBackgroundBuffer;
3562 10 : ReadRasterFunction(&job);
3563 10 : eErr = job.eErr;
3564 : }
3565 :
3566 526 : for (int nBlockYOff = 0; eErr == CE_None && nBlockYOff < nTilesY;
3567 : nBlockYOff++)
3568 : {
3569 682 : for (int nBlockXOff = 0; eErr == CE_None && nBlockXOff < nTilesX;
3570 : nBlockXOff++)
3571 : {
3572 407 : const int nWidthToRead =
3573 407 : std::min(nBlockXSize, nXSize - nBlockXOff * nBlockXSize);
3574 407 : const int nHeightToRead =
3575 407 : std::min(nBlockYSize, nYSize - nBlockYOff * nBlockYSize);
3576 :
3577 407 : if (bUseIOThread)
3578 : {
3579 : // Wait for previous background I/O task to be finished
3580 100 : oPool.WaitCompletion();
3581 100 : eErr = job.eErr;
3582 :
3583 : // Swap buffers
3584 100 : std::swap(pabyBackgroundBuffer, pabyActiveBuffer);
3585 :
3586 : // Prepare for next I/O task
3587 100 : int nNextBlockXOff = nBlockXOff + 1;
3588 100 : int nNextBlockYOff = nBlockYOff;
3589 100 : if (nNextBlockXOff == nTilesX)
3590 : {
3591 26 : nNextBlockXOff = 0;
3592 26 : nNextBlockYOff++;
3593 : }
3594 100 : if (nNextBlockYOff != nTilesY)
3595 : {
3596 90 : job.nXOff = nNextBlockXOff * nBlockXSize;
3597 90 : job.nYOff = nNextBlockYOff * nBlockYSize;
3598 90 : job.nWidthToRead =
3599 90 : std::min(nBlockXSize, nXSize - job.nXOff);
3600 90 : job.nHeightToRead =
3601 90 : std::min(nBlockYSize, nYSize - job.nYOff);
3602 90 : job.pBuffer = pabyBackgroundBuffer;
3603 :
3604 : // Submit next job
3605 90 : oPool.SubmitJob(ReadRasterFunction, &job);
3606 : }
3607 : }
3608 : else
3609 : {
3610 307 : job.nXOff = nBlockXOff * nBlockXSize;
3611 307 : job.nYOff = nBlockYOff * nBlockYSize;
3612 307 : job.nWidthToRead = nWidthToRead;
3613 307 : job.nHeightToRead = nHeightToRead;
3614 307 : ReadRasterFunction(&job);
3615 307 : eErr = job.eErr;
3616 : }
3617 :
3618 407 : if (b1BitAlpha)
3619 : {
3620 64987 : for (int i = 0; i < nWidthToRead * nHeightToRead; i++)
3621 : {
3622 64984 : if (pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3623 64984 : nHeightToRead +
3624 : i])
3625 25040 : pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3626 25040 : nHeightToRead +
3627 25040 : i] = 1;
3628 : else
3629 39944 : pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3630 39944 : nHeightToRead +
3631 39944 : i] = 0;
3632 : }
3633 : }
3634 407 : if (eErr == CE_None)
3635 : {
3636 407 : if (bYCBCR420)
3637 : {
3638 202 : for (int j = 0; j < nHeightToRead; j++)
3639 : {
3640 27000 : for (int i = 0; i < nWidthToRead; i++)
3641 : {
3642 26800 : const int R =
3643 26800 : pabyActiveBuffer[j * nWidthToRead + i];
3644 26800 : const int G =
3645 26800 : pabyActiveBuffer[nHeightToRead *
3646 26800 : nWidthToRead +
3647 26800 : j * nWidthToRead + i];
3648 26800 : const int B =
3649 26800 : pabyActiveBuffer[2 * nHeightToRead *
3650 26800 : nWidthToRead +
3651 26800 : j * nWidthToRead + i];
3652 26800 : const int Y = static_cast<int>(
3653 26800 : 0.299 * R + 0.587 * G + 0.114 * B);
3654 53600 : const int Cb = CLAMP_0_255(static_cast<int>(
3655 26800 : -0.1687 * R - 0.3313 * G + 0.5 * B + 128));
3656 53600 : const int Cr = CLAMP_0_255(static_cast<int>(
3657 26800 : 0.5 * R - 0.4187 * G - 0.0813 * B + 128));
3658 26800 : pYUV420Buffer[j * nWidthToRead + i] =
3659 : static_cast<GByte>(Y);
3660 26800 : pYUV420Buffer[nHeightToRead * nWidthToRead +
3661 26800 : ((j / 2) * ((nWidthToRead) / 2) +
3662 26800 : i / 2)] = static_cast<GByte>(Cb);
3663 26800 : pYUV420Buffer[5 * nHeightToRead * nWidthToRead /
3664 26800 : 4 +
3665 26800 : ((j / 2) * ((nWidthToRead) / 2) +
3666 26800 : i / 2)] = static_cast<GByte>(Cr);
3667 26800 : if (nBands == 4)
3668 : {
3669 24300 : pYUV420Buffer[3 * nHeightToRead *
3670 24300 : nWidthToRead / 2 +
3671 24300 : j * nWidthToRead + i] =
3672 24300 : static_cast<GByte>(
3673 24300 : pabyActiveBuffer[3 * nHeightToRead *
3674 24300 : nWidthToRead +
3675 24300 : j * nWidthToRead +
3676 : i]);
3677 : }
3678 : }
3679 : }
3680 :
3681 2 : int nBytesToWrite =
3682 2 : 3 * nWidthToRead * nHeightToRead / 2;
3683 2 : if (nBands == 4)
3684 1 : nBytesToWrite += nBlockXSize * nBlockYSize;
3685 :
3686 2 : if (!localctx.compressTile(iTile, pYUV420Buffer,
3687 : nBytesToWrite))
3688 : {
3689 0 : CPLError(CE_Failure, CPLE_AppDefined,
3690 : "compress tile failed");
3691 0 : eErr = CE_Failure;
3692 : }
3693 : }
3694 : else
3695 : {
3696 405 : if (!localctx.compressTile(iTile, pabyActiveBuffer,
3697 405 : nWidthToRead *
3698 405 : nHeightToRead * nBands *
3699 : nDataTypeSize))
3700 : {
3701 0 : CPLError(CE_Failure, CPLE_AppDefined,
3702 : "compress tile failed");
3703 0 : eErr = CE_Failure;
3704 : }
3705 : }
3706 : }
3707 :
3708 407 : if (!pfnProgress((iTile + 1) * 1.0 / (nTilesX * nTilesY),
3709 : nullptr, pProgressData))
3710 0 : eErr = CE_Failure;
3711 :
3712 407 : iTile++;
3713 : }
3714 : }
3715 :
3716 251 : if (bUseIOThread && eErr == CE_Failure)
3717 : {
3718 : // Wait for previous background I/O task to be finished
3719 : // before freeing buffers (pTempBuffer, etc.)
3720 0 : oPool.WaitCompletion();
3721 : }
3722 :
3723 251 : VSIFree(pTempBuffer);
3724 251 : VSIFree(pYUV420Buffer);
3725 :
3726 251 : if (eErr != CE_None)
3727 : {
3728 0 : localctx.free();
3729 0 : delete poGMLJP2Box;
3730 0 : return nullptr;
3731 : }
3732 :
3733 251 : if (!localctx.finishCompress())
3734 : {
3735 10 : localctx.free();
3736 10 : delete poGMLJP2Box;
3737 10 : return nullptr;
3738 : }
3739 241 : localctx.free();
3740 : }
3741 :
3742 : /* -------------------------------------------------------------------- */
3743 : /* Patch JP2C box length and add trailing JP2 boxes */
3744 : /* -------------------------------------------------------------------- */
3745 248 : bool bRet = true;
3746 248 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
3747 466 : !BASE::canPerformDirectIO() &&
3748 218 : !CPLFetchBool(papszOptions, "JP2C_LENGTH_ZERO",
3749 : false) /* debug option */)
3750 : {
3751 217 : vsi_l_offset nEndJP2C = VSIFTellL(fp);
3752 217 : GUIntBig nBoxSize = nEndJP2C - nStartJP2C;
3753 217 : if (bUseXLBoxes)
3754 : {
3755 1 : VSIFSeekL(fp, nStartJP2C + 8, SEEK_SET);
3756 1 : CPL_MSBPTR64(&nBoxSize);
3757 1 : if (VSIFWriteL(&nBoxSize, 8, 1, fp) != 1)
3758 0 : bRet = false;
3759 : }
3760 : else
3761 : {
3762 216 : if (nBoxSize > UINT_MAX)
3763 : {
3764 : /* Should not happen hopefully */
3765 0 : if ((bGeoreferencingCompatOfGeoJP2 || poGMLJP2Box) &&
3766 : bGeoBoxesAfter)
3767 : {
3768 0 : CPLError(CE_Warning, CPLE_AppDefined,
3769 : "Cannot write GMLJP2/GeoJP2 boxes as codestream "
3770 : "is unexpectedly > 4GB");
3771 0 : bGeoreferencingCompatOfGeoJP2 = FALSE;
3772 0 : delete poGMLJP2Box;
3773 0 : poGMLJP2Box = nullptr;
3774 : }
3775 : }
3776 : else
3777 : {
3778 216 : VSIFSeekL(fp, nStartJP2C, SEEK_SET);
3779 216 : GUInt32 nBoxSize32 = static_cast<GUInt32>(nBoxSize);
3780 216 : CPL_MSBPTR32(&nBoxSize32);
3781 216 : if (VSIFWriteL(&nBoxSize32, 4, 1, fp) != 1)
3782 0 : bRet = false;
3783 : }
3784 : }
3785 217 : VSIFSeekL(fp, 0, SEEK_END);
3786 :
3787 217 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3788 : {
3789 14 : if (!WriteIPRBox(fp, poSrcDS))
3790 0 : bRet = false;
3791 : }
3792 :
3793 217 : if (bGeoBoxesAfter)
3794 : {
3795 11 : if (poGMLJP2Box != nullptr)
3796 : {
3797 5 : if (!WriteBox(fp, poGMLJP2Box))
3798 0 : bRet = false;
3799 : }
3800 :
3801 11 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3802 : {
3803 3 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3804 : {
3805 3 : if (!WriteXMLBoxes(fp, poSrcDS))
3806 0 : bRet = false;
3807 : }
3808 3 : if (!WriteGDALMetadataBox(fp, poSrcDS, papszOptions))
3809 0 : bRet = false;
3810 : }
3811 :
3812 11 : if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
3813 : {
3814 5 : GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
3815 5 : if (!WriteBox(fp, poBox))
3816 0 : bRet = false;
3817 5 : delete poBox;
3818 : }
3819 :
3820 14 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
3821 3 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3822 : {
3823 3 : if (!WriteXMPBox(fp, poSrcDS))
3824 0 : bRet = false;
3825 : }
3826 : }
3827 : }
3828 :
3829 248 : if (fpOwner)
3830 : {
3831 248 : if (VSIFCloseL(fpOwner.release()) != 0)
3832 0 : bRet = false;
3833 : }
3834 248 : delete poGMLJP2Box;
3835 248 : if (!bRet)
3836 0 : return nullptr;
3837 :
3838 : /* -------------------------------------------------------------------- */
3839 : /* Re-open dataset, and copy any auxiliary pam information. */
3840 : /* -------------------------------------------------------------------- */
3841 :
3842 248 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
3843 3 : auto poDS =
3844 248 : dynamic_cast<JP2OPJLikeDataset *>(JP2OPJLikeDataset::Open(&oOpenInfo));
3845 :
3846 248 : if (poDS)
3847 : {
3848 245 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT & (~GCIF_METADATA));
3849 :
3850 : /* Only write relevant metadata to PAM, and if needed */
3851 245 : if (!CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3852 : {
3853 231 : char **papszSrcMD = CSLDuplicate(poSrcDS->GetMetadata());
3854 : papszSrcMD =
3855 231 : CSLSetNameValue(papszSrcMD, GDALMD_AREA_OR_POINT, nullptr);
3856 231 : papszSrcMD = CSLSetNameValue(papszSrcMD, "Corder", nullptr);
3857 322 : for (char **papszSrcMDIter = papszSrcMD;
3858 322 : papszSrcMDIter && *papszSrcMDIter;)
3859 : {
3860 : /* Remove entries like KEY= (without value) */
3861 91 : if ((*papszSrcMDIter)[0] &&
3862 91 : (*papszSrcMDIter)[strlen((*papszSrcMDIter)) - 1] == '=')
3863 : {
3864 37 : CPLFree(*papszSrcMDIter);
3865 37 : memmove(papszSrcMDIter, papszSrcMDIter + 1,
3866 : sizeof(char *) *
3867 37 : (CSLCount(papszSrcMDIter + 1) + 1));
3868 : }
3869 : else
3870 54 : ++papszSrcMDIter;
3871 : }
3872 231 : char **papszMD = CSLDuplicate(poDS->GetMetadata());
3873 231 : papszMD = CSLSetNameValue(papszMD, GDALMD_AREA_OR_POINT, nullptr);
3874 246 : if (papszSrcMD && papszSrcMD[0] != nullptr &&
3875 15 : CSLCount(papszSrcMD) != CSLCount(papszMD))
3876 : {
3877 9 : poDS->SetMetadata(papszSrcMD);
3878 : }
3879 231 : CSLDestroy(papszSrcMD);
3880 231 : CSLDestroy(papszMD);
3881 : }
3882 : }
3883 :
3884 248 : return poDS;
3885 : }
3886 :
3887 : #ifdef unused
3888 : template <typename CODEC, typename BASE>
3889 : void GDALRegisterJP2(const std::string &libraryName,
3890 : const std::string &driverName)
3891 :
3892 : {
3893 : if (!GDAL_CHECK_VERSION((driverName + " driver").c_str()))
3894 : return;
3895 :
3896 : if (GDALGetDriverByName(driverName.c_str()) != nullptr)
3897 : return;
3898 :
3899 : GDALDriver *poDriver = new GDALDriver();
3900 : poDriver->SetDescription(driverName.c_str());
3901 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3902 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
3903 : poDriver->SetMetadataItem(
3904 : GDAL_DMD_LONGNAME,
3905 : ("JPEG-2000 driver based on " + libraryName + " library").c_str());
3906 :
3907 : poDriver->SetMetadataItem(
3908 : GDAL_DMD_HELPTOPIC,
3909 : ("drivers/raster/jp2" + CPLString(libraryName).tolower() + ".html")
3910 : .c_str());
3911 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jp2");
3912 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jp2");
3913 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "jp2 j2k");
3914 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
3915 : "Byte Int16 UInt16 Int32 UInt32");
3916 :
3917 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3918 : BASE::setMetaData(poDriver);
3919 :
3920 : poDriver->pfnIdentify = JP2OPJLikeDataset<CODEC, BASE>::Identify;
3921 : poDriver->pfnOpen = JP2OPJLikeDataset<CODEC, BASE>::Open;
3922 : poDriver->pfnCreateCopy = JP2OPJLikeDataset<CODEC, BASE>::CreateCopy;
3923 :
3924 : GetGDALDriverManager()->RegisterDriver(poDriver);
3925 : }
3926 : #endif
|