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