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