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