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