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 17 : void JP2OPJLikeDataset<CODEC, BASE>::ReadBlockInThread(void *userdata)
201 : {
202 : int nPair;
203 17 : auto poJob = static_cast<JP2JobStruct<CODEC, BASE> *>(userdata);
204 :
205 17 : JP2OPJLikeDataset *poGDS = poJob->poGDS_;
206 17 : const int nBand = poJob->nBand;
207 17 : 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 24394 : GDALCopyWords(
683 24394 : localctx.psImage->comps[iBand - 1].data +
684 12197 : 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(m_gt);
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 1916 : int JP2OPJLikeDataset<CODEC, BASE>::CloseDependentDatasets()
1091 : {
1092 1916 : int bRet = GDALJP2AbstractDataset::CloseDependentDatasets();
1093 1916 : 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 1916 : 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 : CPLErr
1130 24 : JP2OPJLikeDataset<CODEC, BASE>::SetGeoTransform(const GDALGeoTransform >)
1131 : {
1132 24 : if (eAccess == GA_Update)
1133 : {
1134 4 : this->bRewrite = TRUE;
1135 4 : m_gt = gt;
1136 4 : bGeoTransformValid = m_gt != GDALGeoTransform();
1137 4 : return CE_None;
1138 : }
1139 : else
1140 20 : return GDALJP2AbstractDataset::SetGeoTransform(gt);
1141 : }
1142 :
1143 : /************************************************************************/
1144 : /* SetGCPs() */
1145 : /************************************************************************/
1146 :
1147 : template <typename CODEC, typename BASE>
1148 4 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetGCPs(int nGCPCountIn,
1149 : const GDAL_GCP *pasGCPListIn,
1150 : const OGRSpatialReference *poSRS)
1151 : {
1152 4 : if (eAccess == GA_Update)
1153 : {
1154 3 : this->bRewrite = TRUE;
1155 3 : if (nGCPCount > 0)
1156 : {
1157 1 : GDALDeinitGCPs(nGCPCount, pasGCPList);
1158 1 : CPLFree(pasGCPList);
1159 : }
1160 :
1161 3 : m_oSRS.Clear();
1162 3 : if (poSRS)
1163 2 : m_oSRS = *poSRS;
1164 :
1165 3 : nGCPCount = nGCPCountIn;
1166 3 : pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
1167 :
1168 3 : return CE_None;
1169 : }
1170 : else
1171 1 : return GDALJP2AbstractDataset::SetGCPs(nGCPCountIn, pasGCPListIn,
1172 1 : poSRS);
1173 : }
1174 :
1175 : /************************************************************************/
1176 : /* SetMetadata() */
1177 : /************************************************************************/
1178 :
1179 : template <typename CODEC, typename BASE>
1180 11 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetMetadata(char **papszMetadata,
1181 : const char *pszDomain)
1182 : {
1183 11 : if (eAccess == GA_Update)
1184 : {
1185 2 : this->bRewrite = TRUE;
1186 2 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
1187 : {
1188 1 : CSLDestroy(m_papszMainMD);
1189 1 : m_papszMainMD = CSLDuplicate(papszMetadata);
1190 : }
1191 2 : return GDALDataset::SetMetadata(papszMetadata, pszDomain);
1192 : }
1193 9 : return GDALJP2AbstractDataset::SetMetadata(papszMetadata, pszDomain);
1194 : }
1195 :
1196 : /************************************************************************/
1197 : /* SetMetadata() */
1198 : /************************************************************************/
1199 :
1200 : template <typename CODEC, typename BASE>
1201 3 : CPLErr JP2OPJLikeDataset<CODEC, BASE>::SetMetadataItem(const char *pszName,
1202 : const char *pszValue,
1203 : const char *pszDomain)
1204 : {
1205 3 : if (eAccess == GA_Update)
1206 : {
1207 1 : this->bRewrite = TRUE;
1208 1 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
1209 : {
1210 1 : m_papszMainMD = CSLSetNameValue(GetMetadata(), pszName, pszValue);
1211 : }
1212 1 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1213 : }
1214 2 : return GDALJP2AbstractDataset::SetMetadataItem(pszName, pszValue,
1215 2 : pszDomain);
1216 : }
1217 :
1218 : /************************************************************************/
1219 : /* Identify() */
1220 : /************************************************************************/
1221 :
1222 : #ifndef jpc_header_defined
1223 : #define jpc_header_defined
1224 : static const unsigned char jpc_header[] = {0xff, 0x4f, 0xff,
1225 : 0x51}; // SOC + RSIZ markers
1226 : static const unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP ' */
1227 : #endif
1228 :
1229 : template <typename CODEC, typename BASE>
1230 766 : int JP2OPJLikeDataset<CODEC, BASE>::Identify(GDALOpenInfo *poOpenInfo)
1231 :
1232 : {
1233 766 : if (poOpenInfo->nHeaderBytes >= 16 &&
1234 766 : (memcmp(poOpenInfo->pabyHeader, jpc_header, sizeof(jpc_header)) == 0 ||
1235 683 : memcmp(poOpenInfo->pabyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) ==
1236 : 0))
1237 766 : return TRUE;
1238 :
1239 : else
1240 0 : return FALSE;
1241 : }
1242 :
1243 : /************************************************************************/
1244 : /* JP2FindCodeStream() */
1245 : /************************************************************************/
1246 :
1247 773 : static vsi_l_offset JP2FindCodeStream(VSILFILE *fp, vsi_l_offset *pnLength)
1248 : {
1249 773 : vsi_l_offset nCodeStreamStart = 0;
1250 773 : vsi_l_offset nCodeStreamLength = 0;
1251 :
1252 773 : VSIFSeekL(fp, 0, SEEK_SET);
1253 : GByte abyHeader[16];
1254 773 : VSIFReadL(abyHeader, 1, 16, fp);
1255 :
1256 773 : if (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0)
1257 : {
1258 83 : VSIFSeekL(fp, 0, SEEK_END);
1259 83 : nCodeStreamLength = VSIFTellL(fp);
1260 : }
1261 690 : else if (memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) == 0)
1262 : {
1263 : /* Find offset of first jp2c box */
1264 1378 : GDALJP2Box oBox(fp);
1265 689 : if (oBox.ReadFirst())
1266 : {
1267 3585 : while (strlen(oBox.GetType()) > 0)
1268 : {
1269 3585 : if (EQUAL(oBox.GetType(), "jp2c"))
1270 : {
1271 688 : nCodeStreamStart = VSIFTellL(fp);
1272 688 : nCodeStreamLength = oBox.GetDataLength();
1273 688 : break;
1274 : }
1275 :
1276 2897 : if (!oBox.ReadNext())
1277 1 : break;
1278 : }
1279 : }
1280 : }
1281 773 : *pnLength = nCodeStreamLength;
1282 773 : return nCodeStreamStart;
1283 : }
1284 :
1285 : /************************************************************************/
1286 : /* Open() */
1287 : /************************************************************************/
1288 :
1289 : template <typename CODEC, typename BASE>
1290 766 : GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::Open(GDALOpenInfo *poOpenInfo)
1291 :
1292 : {
1293 766 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
1294 0 : return nullptr;
1295 :
1296 : /* Detect which codec to use : J2K or JP2 ? */
1297 766 : vsi_l_offset nCodeStreamLength = 0;
1298 : vsi_l_offset nCodeStreamStart =
1299 766 : JP2FindCodeStream(poOpenInfo->fpL, &nCodeStreamLength);
1300 :
1301 766 : if (nCodeStreamStart == 0 && nCodeStreamLength == 0)
1302 : {
1303 1 : CPLError(CE_Failure, CPLE_AppDefined, "No code-stream in JP2 file");
1304 1 : return nullptr;
1305 : }
1306 1530 : JP2OPJLikeDataset oTmpDS;
1307 765 : int numThreads = oTmpDS.GetNumThreads();
1308 765 : auto eCodecFormat = (nCodeStreamStart == 0) ? CODEC::cvtenum(JP2_CODEC_J2K)
1309 682 : : CODEC::cvtenum(JP2_CODEC_JP2);
1310 :
1311 765 : uint32_t nTileW = 0, nTileH = 0;
1312 765 : int numResolutions = 0;
1313 765 : CODEC localctx;
1314 765 : localctx.open(poOpenInfo->fpL, nCodeStreamStart);
1315 765 : if (!localctx.setUpDecompress(numThreads, nCodeStreamLength, &nTileW,
1316 : &nTileH, &numResolutions))
1317 6 : return nullptr;
1318 :
1319 759 : GDALDataType eDataType = GDT_Byte;
1320 759 : if (localctx.psImage->comps[0].prec > 16)
1321 : {
1322 0 : if (localctx.psImage->comps[0].sgnd)
1323 0 : eDataType = GDT_Int32;
1324 : else
1325 0 : eDataType = GDT_UInt32;
1326 : }
1327 759 : else if (localctx.psImage->comps[0].prec > 8)
1328 : {
1329 26 : if (localctx.psImage->comps[0].sgnd)
1330 7 : eDataType = GDT_Int16;
1331 : else
1332 19 : eDataType = GDT_UInt16;
1333 : }
1334 :
1335 759 : int bIs420 =
1336 1518 : (localctx.psImage->color_space != CODEC::cvtenum(JP2_CLRSPC_SRGB) &&
1337 733 : eDataType == GDT_Byte &&
1338 733 : (localctx.psImage->numcomps == 3 || localctx.psImage->numcomps == 4) &&
1339 58 : localctx.psImage->comps[1].w == localctx.psImage->comps[0].w / 2 &&
1340 3 : localctx.psImage->comps[1].h == localctx.psImage->comps[0].h / 2 &&
1341 3 : localctx.psImage->comps[2].w == localctx.psImage->comps[0].w / 2 &&
1342 1521 : localctx.psImage->comps[2].h == localctx.psImage->comps[0].h / 2) &&
1343 3 : (localctx.psImage->numcomps == 3 ||
1344 2 : (localctx.psImage->numcomps == 4 &&
1345 2 : localctx.psImage->comps[3].w == localctx.psImage->comps[0].w &&
1346 2 : localctx.psImage->comps[3].h == localctx.psImage->comps[0].h));
1347 :
1348 759 : if (bIs420)
1349 : {
1350 3 : CPLDebug(CODEC::debugId(), "420 format");
1351 : }
1352 : else
1353 : {
1354 917 : for (unsigned iBand = 2; iBand <= localctx.psImage->numcomps; iBand++)
1355 : {
1356 161 : if (localctx.psImage->comps[iBand - 1].w !=
1357 161 : localctx.psImage->comps[0].w ||
1358 161 : localctx.psImage->comps[iBand - 1].h !=
1359 161 : localctx.psImage->comps[0].h)
1360 : {
1361 0 : CPLDebug(CODEC::debugId(), "Unable to handle that image (2)");
1362 0 : localctx.free();
1363 0 : return nullptr;
1364 : }
1365 : }
1366 : }
1367 :
1368 : /* -------------------------------------------------------------------- */
1369 : /* Create a corresponding GDALDataset. */
1370 : /* -------------------------------------------------------------------- */
1371 : JP2OPJLikeDataset *poDS;
1372 : int iBand;
1373 :
1374 759 : poDS = new JP2OPJLikeDataset();
1375 759 : poDS->m_osFilename = poOpenInfo->pszFilename;
1376 759 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
1377 678 : poDS->eAccess = poOpenInfo->eAccess;
1378 759 : poDS->eColorSpace = localctx.psImage->color_space;
1379 759 : poDS->nRasterXSize = localctx.psImage->x1 - localctx.psImage->x0;
1380 759 : poDS->nRasterYSize = localctx.psImage->y1 - localctx.psImage->y0;
1381 759 : poDS->nBands = localctx.psImage->numcomps;
1382 759 : poDS->fp_ = poOpenInfo->fpL;
1383 759 : poOpenInfo->fpL = nullptr;
1384 759 : poDS->nCodeStreamStart = nCodeStreamStart;
1385 759 : poDS->nCodeStreamLength = nCodeStreamLength;
1386 759 : poDS->bIs420 = bIs420;
1387 1468 : poDS->bSingleTiled = (poDS->nRasterXSize == static_cast<int>(nTileW) &&
1388 709 : poDS->nRasterYSize == static_cast<int>(nTileH));
1389 759 : poDS->m_nX0 = localctx.psImage->x0;
1390 759 : poDS->m_nY0 = localctx.psImage->y0;
1391 759 : poDS->m_nTileWidth = nTileW;
1392 759 : poDS->m_nTileHeight = nTileH;
1393 :
1394 759 : int nBlockXSize = static_cast<int>(nTileW);
1395 759 : int nBlockYSize = static_cast<int>(nTileH);
1396 :
1397 759 : if (CPLFetchBool(poOpenInfo->papszOpenOptions, "USE_TILE_AS_BLOCK", false))
1398 : {
1399 0 : poDS->bUseSetDecodeArea = false;
1400 : }
1401 :
1402 759 : poDS->m_bStrict = CPLTestBool(
1403 759 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "STRICT", "YES"));
1404 759 : localctx.updateStrict(poDS->m_bStrict);
1405 :
1406 759 : if (localctx.preferPerBlockDeCompress())
1407 : {
1408 : /* Some Sentinel2 preview datasets are 343x343 large, but with 8x8 blocks */
1409 : /* Using the tile API for that is super slow, so expose a single block */
1410 759 : if (poDS->nRasterXSize <= 1024 && poDS->nRasterYSize <= 1024 &&
1411 743 : nTileW < 32 && nTileH < 32)
1412 : {
1413 574 : poDS->bUseSetDecodeArea = true;
1414 574 : nBlockXSize = poDS->nRasterXSize;
1415 574 : nBlockYSize = poDS->nRasterYSize;
1416 : }
1417 : else
1418 : {
1419 185 : poDS->bUseSetDecodeArea =
1420 322 : poDS->bSingleTiled &&
1421 137 : (poDS->nRasterXSize > 1024 || poDS->nRasterYSize > 1024);
1422 :
1423 : /* Other Sentinel2 preview datasets are 343x343 and 60m are 1830x1830,
1424 : * but they */
1425 : /* are tiled with tile dimensions 2048x2048. It would be a waste of */
1426 : /* memory to allocate such big blocks */
1427 185 : if (poDS->nRasterXSize < static_cast<int>(nTileW) &&
1428 18 : poDS->nRasterYSize < static_cast<int>(nTileH))
1429 : {
1430 18 : poDS->bUseSetDecodeArea = TRUE;
1431 18 : nBlockXSize = poDS->nRasterXSize;
1432 18 : nBlockYSize = poDS->nRasterYSize;
1433 18 : if (nBlockXSize > 2048)
1434 0 : nBlockXSize = 2048;
1435 18 : if (nBlockYSize > 2048)
1436 0 : nBlockYSize = 2048;
1437 : }
1438 167 : else if (poDS->bUseSetDecodeArea)
1439 : {
1440 : // Arbitrary threshold... ~4 million at least needed for the GRIB2
1441 : // images mentioned below.
1442 7 : if (nTileH == 1 && nTileW < 20 * 1024 * 1024)
1443 : {
1444 : // Some GRIB2 JPEG2000 compressed images are a 2D image
1445 : // organized as a single line image...
1446 : }
1447 : else
1448 : {
1449 7 : if (nBlockXSize > 1024)
1450 7 : nBlockXSize = 1024;
1451 7 : if (nBlockYSize > 1024)
1452 7 : nBlockYSize = 1024;
1453 : }
1454 : }
1455 : }
1456 : }
1457 :
1458 759 : GDALColorTable *poCT = nullptr;
1459 :
1460 : /* -------------------------------------------------------------------- */
1461 : /* Look for color table or cdef box */
1462 : /* -------------------------------------------------------------------- */
1463 759 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
1464 : {
1465 678 : vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
1466 :
1467 1356 : GDALJP2Box oBox(poDS->fp_);
1468 678 : if (oBox.ReadFirst())
1469 : {
1470 3625 : while (strlen(oBox.GetType()) > 0)
1471 : {
1472 3625 : if (EQUAL(oBox.GetType(), "jp2h"))
1473 : {
1474 1356 : GDALJP2Box oSubBox(poDS->fp_);
1475 :
1476 2146 : for (oSubBox.ReadFirstChild(&oBox);
1477 2146 : strlen(oSubBox.GetType()) > 0;
1478 1468 : oSubBox.ReadNextChild(&oBox))
1479 : {
1480 1468 : GIntBig nDataLength = oSubBox.GetDataLength();
1481 2911 : if (poCT == nullptr &&
1482 1443 : EQUAL(oSubBox.GetType(), "pclr") &&
1483 2911 : nDataLength >= 3 &&
1484 : nDataLength <= 2 + 1 + 4 + 4 * 256)
1485 : {
1486 24 : GByte *pabyCT = oSubBox.ReadBoxData();
1487 24 : if (pabyCT != nullptr)
1488 : {
1489 24 : int nEntries = (pabyCT[0] << 8) | pabyCT[1];
1490 24 : int nComponents = pabyCT[2];
1491 : /* CPLDebug(CODEC::debugId(), "Color table found"); */
1492 24 : if (nEntries <= 256 && nComponents == 3)
1493 : {
1494 : /*CPLDebug(CODEC::debugId(), "resol[0] = %d",
1495 : pabyCT[3]); CPLDebug(CODEC::debugId(), "resol[1] =
1496 : %d", pabyCT[4]); CPLDebug(CODEC::debugId(),
1497 : "resol[2] = %d", pabyCT[5]);*/
1498 19 : if (pabyCT[3] == 7 && pabyCT[4] == 7 &&
1499 19 : pabyCT[5] == 7 &&
1500 19 : nDataLength == 2 + 1 + 3 + 3 * nEntries)
1501 : {
1502 19 : poCT = new GDALColorTable();
1503 1103 : for (int i = 0; i < nEntries; i++)
1504 : {
1505 : GDALColorEntry sEntry;
1506 1084 : sEntry.c1 = pabyCT[6 + 3 * i];
1507 1084 : sEntry.c2 = pabyCT[6 + 3 * i + 1];
1508 1084 : sEntry.c3 = pabyCT[6 + 3 * i + 2];
1509 1084 : sEntry.c4 = 255;
1510 1084 : poCT->SetColorEntry(i, &sEntry);
1511 : }
1512 19 : }
1513 : }
1514 5 : else if (nEntries <= 256 && nComponents == 4)
1515 : {
1516 5 : if (pabyCT[3] == 7 && pabyCT[4] == 7 &&
1517 5 : pabyCT[5] == 7 && pabyCT[6] == 7 &&
1518 5 : nDataLength == 2 + 1 + 4 + 4 * nEntries)
1519 : {
1520 5 : poCT = new GDALColorTable();
1521 17 : for (int i = 0; i < nEntries; i++)
1522 : {
1523 : GDALColorEntry sEntry;
1524 12 : sEntry.c1 = pabyCT[7 + 4 * i];
1525 12 : sEntry.c2 = pabyCT[7 + 4 * i + 1];
1526 12 : sEntry.c3 = pabyCT[7 + 4 * i + 2];
1527 12 : sEntry.c4 = pabyCT[7 + 4 * i + 3];
1528 12 : poCT->SetColorEntry(i, &sEntry);
1529 : }
1530 : }
1531 : }
1532 24 : CPLFree(pabyCT);
1533 : }
1534 : }
1535 : /* There's a bug/misfeature in openjpeg: the color_space
1536 : only gets set at read tile time */
1537 1444 : else if (EQUAL(oSubBox.GetType(), "colr") &&
1538 : nDataLength == 7)
1539 : {
1540 678 : GByte *pabyContent = oSubBox.ReadBoxData();
1541 678 : if (pabyContent != nullptr)
1542 : {
1543 678 : if (pabyContent[0] ==
1544 : 1 /* enumerated colourspace */)
1545 : {
1546 678 : GUInt32 enumcs = (pabyContent[3] << 24) |
1547 678 : (pabyContent[4] << 16) |
1548 678 : (pabyContent[5] << 8) |
1549 : (pabyContent[6]);
1550 678 : if (enumcs == 16)
1551 : {
1552 53 : poDS->eColorSpace =
1553 53 : CODEC::cvtenum(JP2_CLRSPC_SRGB);
1554 53 : CPLDebug(CODEC::debugId(),
1555 : "SRGB color space");
1556 : }
1557 625 : else if (enumcs == 17)
1558 : {
1559 618 : poDS->eColorSpace =
1560 618 : CODEC::cvtenum(JP2_CLRSPC_GRAY);
1561 618 : CPLDebug(CODEC::debugId(),
1562 : "Grayscale color space");
1563 : }
1564 7 : else if (enumcs == 18)
1565 : {
1566 3 : poDS->eColorSpace =
1567 3 : CODEC::cvtenum(JP2_CLRSPC_SYCC);
1568 3 : CPLDebug(CODEC::debugId(),
1569 : "SYCC color space");
1570 : }
1571 4 : else if (enumcs == 20)
1572 : {
1573 : /* Used by
1574 : * J2KP4files/testfiles_jp2/file7.jp2 */
1575 0 : poDS->eColorSpace =
1576 0 : CODEC::cvtenum(JP2_CLRSPC_SRGB);
1577 0 : CPLDebug(CODEC::debugId(),
1578 : "e-sRGB color space");
1579 : }
1580 4 : else if (enumcs == 21)
1581 : {
1582 : /* Used by
1583 : * J2KP4files/testfiles_jp2/file5.jp2 */
1584 0 : poDS->eColorSpace =
1585 0 : CODEC::cvtenum(JP2_CLRSPC_SRGB);
1586 0 : CPLDebug(CODEC::debugId(),
1587 : "ROMM-RGB color space");
1588 : }
1589 : else
1590 : {
1591 4 : poDS->eColorSpace =
1592 4 : CODEC::cvtenum(JP2_CLRSPC_UNKNOWN);
1593 4 : CPLDebug(CODEC::debugId(),
1594 : "Unknown color space");
1595 : }
1596 : }
1597 678 : CPLFree(pabyContent);
1598 : }
1599 : }
1600 : /* Check if there's an alpha channel or odd channel
1601 : * attribution */
1602 796 : else if (EQUAL(oSubBox.GetType(), "cdef") &&
1603 30 : nDataLength == 2 + poDS->nBands * 6)
1604 : {
1605 29 : GByte *pabyContent = oSubBox.ReadBoxData();
1606 29 : if (pabyContent != nullptr)
1607 : {
1608 29 : int nEntries =
1609 29 : (pabyContent[0] << 8) | pabyContent[1];
1610 29 : if (nEntries == poDS->nBands)
1611 : {
1612 29 : poDS->nRedIndex = -1;
1613 29 : poDS->nGreenIndex = -1;
1614 29 : poDS->nBlueIndex = -1;
1615 130 : for (int i = 0; i < poDS->nBands; i++)
1616 : {
1617 101 : int CNi =
1618 101 : (pabyContent[2 + 6 * i] << 8) |
1619 101 : pabyContent[2 + 6 * i + 1];
1620 101 : int Typi =
1621 101 : (pabyContent[2 + 6 * i + 2] << 8) |
1622 101 : pabyContent[2 + 6 * i + 3];
1623 101 : int Asoci =
1624 101 : (pabyContent[2 + 6 * i + 4] << 8) |
1625 101 : pabyContent[2 + 6 * i + 5];
1626 101 : if (CNi < 0 || CNi >= poDS->nBands)
1627 : {
1628 0 : CPLError(CE_Failure,
1629 : CPLE_AppDefined,
1630 : "Wrong value of CN%d=%d",
1631 : i, CNi);
1632 0 : break;
1633 : }
1634 101 : if (Typi == 0)
1635 : {
1636 75 : if (Asoci == 1)
1637 24 : poDS->nRedIndex = CNi;
1638 51 : else if (Asoci == 2)
1639 18 : poDS->nGreenIndex = CNi;
1640 33 : else if (Asoci == 3)
1641 18 : poDS->nBlueIndex = CNi;
1642 15 : else if (Asoci < 0 ||
1643 15 : (Asoci > poDS->nBands &&
1644 : Asoci != 65535))
1645 : {
1646 0 : CPLError(
1647 : CE_Failure, CPLE_AppDefined,
1648 : "Wrong value of Asoc%d=%d",
1649 : i, Asoci);
1650 0 : break;
1651 : }
1652 : }
1653 26 : else if (Typi == 1)
1654 : {
1655 26 : poDS->nAlphaIndex = CNi;
1656 : }
1657 : }
1658 : }
1659 : else
1660 : {
1661 0 : CPLDebug(CODEC::debugId(),
1662 : "Unsupported cdef content");
1663 : }
1664 29 : CPLFree(pabyContent);
1665 : }
1666 : }
1667 : }
1668 : }
1669 :
1670 3625 : if (!oBox.ReadNext())
1671 678 : break;
1672 : }
1673 : }
1674 :
1675 678 : VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
1676 :
1677 678 : if (poDS->eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
1678 618 : poDS->nBands == 4 && poDS->nRedIndex == 0 &&
1679 1301 : poDS->nGreenIndex == 1 && poDS->nBlueIndex == 2 &&
1680 5 : poDS->m_osFilename.find("dop10rgbi") != std::string::npos)
1681 : {
1682 0 : CPLDebug(CODEC::debugId(),
1683 : "Autofix wrong colorspace from Greyscale to sRGB");
1684 : // Workaround https://github.com/uclouvain/openjpeg/issues/1464
1685 : // dop10rgbi products from https://www.opengeodata.nrw.de/produkte/geobasis/lusat/dop/dop_jp2_f10/
1686 : // have a wrong color space.
1687 0 : poDS->eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
1688 : }
1689 : }
1690 :
1691 : /* -------------------------------------------------------------------- */
1692 : /* Create band information objects. */
1693 : /* -------------------------------------------------------------------- */
1694 1687 : for (iBand = 1; iBand <= poDS->nBands; iBand++)
1695 : {
1696 928 : const bool bPromoteTo8Bit =
1697 954 : iBand == poDS->nAlphaIndex + 1 &&
1698 26 : localctx.psImage
1699 26 : ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1) ? 1
1700 : : 0]
1701 26 : .prec == 8 &&
1702 968 : localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
1703 14 : CPLFetchBool(poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
1704 14 : CPLTestBool(CPLGetConfigOption(
1705 : "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
1706 928 : if (bPromoteTo8Bit)
1707 10 : CPLDebug(CODEC::debugId(),
1708 : "Alpha band is promoted from 1 bit to 8 bit");
1709 :
1710 1846 : auto poBand = new JP2OPJLikeRasterBand<CODEC, BASE>(
1711 : poDS, iBand, eDataType,
1712 918 : bPromoteTo8Bit ? 8 : localctx.psImage->comps[iBand - 1].prec,
1713 : bPromoteTo8Bit, nBlockXSize, nBlockYSize);
1714 928 : if (iBand == 1 && poCT != nullptr)
1715 24 : poBand->poCT = poCT;
1716 928 : poDS->SetBand(iBand, poBand);
1717 : }
1718 :
1719 : /* -------------------------------------------------------------------- */
1720 : /* Create overview datasets. */
1721 : /* -------------------------------------------------------------------- */
1722 759 : int nW = poDS->nRasterXSize;
1723 759 : int nH = poDS->nRasterYSize;
1724 759 : poDS->nParentXSize = poDS->nRasterXSize;
1725 759 : poDS->nParentYSize = poDS->nRasterYSize;
1726 :
1727 : /* Lower resolutions are not compatible with a color-table */
1728 759 : if (poCT != nullptr)
1729 24 : numResolutions = 0;
1730 :
1731 759 : if (poDS->bSingleTiled && poDS->bUseSetDecodeArea)
1732 : {
1733 579 : poDS->cacheNew(&localctx);
1734 : }
1735 759 : poDS->m_pnLastLevel = new int(-1);
1736 :
1737 117 : while (
1738 876 : poDS->nOverviewCount + 1 < numResolutions && (nW > 128 || nH > 128) &&
1739 120 : (poDS->bUseSetDecodeArea || ((nTileW % 2) == 0 && (nTileH % 2) == 0)))
1740 : {
1741 : // This must be this exact formula per the JPEG2000 standard
1742 117 : nW = (nW + 1) / 2;
1743 117 : nH = (nH + 1) / 2;
1744 :
1745 117 : poDS->papoOverviewDS = static_cast<JP2OPJLikeDataset<CODEC, BASE> **>(
1746 234 : CPLRealloc(poDS->papoOverviewDS,
1747 117 : (poDS->nOverviewCount + 1) *
1748 : sizeof(JP2OPJLikeDataset<CODEC, BASE> *)));
1749 117 : JP2OPJLikeDataset *poODS = new JP2OPJLikeDataset();
1750 117 : poODS->m_osFilename = poDS->m_osFilename;
1751 117 : poODS->nParentXSize = poDS->nRasterXSize;
1752 117 : poODS->nParentYSize = poDS->nRasterYSize;
1753 117 : poODS->SetDescription(poOpenInfo->pszFilename);
1754 117 : poODS->iLevel = poDS->nOverviewCount + 1;
1755 117 : poODS->bSingleTiled = poDS->bSingleTiled;
1756 117 : poODS->bUseSetDecodeArea = poDS->bUseSetDecodeArea;
1757 117 : poODS->nRedIndex = poDS->nRedIndex;
1758 117 : poODS->nGreenIndex = poDS->nGreenIndex;
1759 117 : poODS->nBlueIndex = poDS->nBlueIndex;
1760 117 : poODS->nAlphaIndex = poDS->nAlphaIndex;
1761 117 : if (!poDS->bUseSetDecodeArea)
1762 : {
1763 83 : nTileW /= 2;
1764 83 : nTileH /= 2;
1765 83 : nBlockXSize = static_cast<int>(nTileW);
1766 83 : nBlockYSize = static_cast<int>(nTileH);
1767 : }
1768 : else
1769 : {
1770 34 : nBlockXSize = std::min(nW, static_cast<int>(nTileW));
1771 34 : nBlockYSize = std::min(nH, static_cast<int>(nTileH));
1772 : }
1773 :
1774 117 : poODS->eColorSpace = poDS->eColorSpace;
1775 117 : poODS->nRasterXSize = nW;
1776 117 : poODS->nRasterYSize = nH;
1777 117 : poODS->nBands = poDS->nBands;
1778 117 : poODS->fp_ = poDS->fp_;
1779 117 : poODS->nCodeStreamStart = nCodeStreamStart;
1780 117 : poODS->nCodeStreamLength = nCodeStreamLength;
1781 117 : poODS->bIs420 = bIs420;
1782 :
1783 117 : if (poODS->bSingleTiled && poODS->bUseSetDecodeArea)
1784 : {
1785 32 : poODS->cache(poDS);
1786 : }
1787 117 : poODS->m_pnLastLevel = poDS->m_pnLastLevel;
1788 117 : poODS->m_bStrict = poDS->m_bStrict;
1789 :
1790 117 : poODS->m_nX0 = poDS->m_nX0;
1791 117 : poODS->m_nY0 = poDS->m_nY0;
1792 :
1793 335 : for (iBand = 1; iBand <= poDS->nBands; iBand++)
1794 : {
1795 218 : const bool bPromoteTo8Bit =
1796 237 : iBand == poDS->nAlphaIndex + 1 &&
1797 19 : localctx.psImage
1798 19 : ->comps[(poDS->nAlphaIndex == 0 && poDS->nBands > 1)
1799 : ? 1
1800 : : 0]
1801 19 : .prec == 8 &&
1802 246 : localctx.psImage->comps[poDS->nAlphaIndex].prec == 1 &&
1803 9 : CPLFetchBool(
1804 9 : poOpenInfo->papszOpenOptions, "1BIT_ALPHA_PROMOTION",
1805 9 : CPLTestBool(CPLGetConfigOption(
1806 : "JP2OPENJPEG_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
1807 :
1808 218 : poODS->SetBand(iBand,
1809 430 : new JP2OPJLikeRasterBand<CODEC, BASE>(
1810 : poODS, iBand, eDataType,
1811 : bPromoteTo8Bit
1812 : ? 8
1813 212 : : localctx.psImage->comps[iBand - 1].prec,
1814 : bPromoteTo8Bit, nBlockXSize, nBlockYSize));
1815 : }
1816 :
1817 117 : poDS->papoOverviewDS[poDS->nOverviewCount++] = poODS;
1818 : }
1819 :
1820 759 : poDS->openCompleteJP2(&localctx);
1821 :
1822 : /* -------------------------------------------------------------------- */
1823 : /* More metadata. */
1824 : /* -------------------------------------------------------------------- */
1825 759 : if (poDS->nBands > 1)
1826 : {
1827 72 : poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
1828 : "IMAGE_STRUCTURE");
1829 : }
1830 :
1831 759 : poOpenInfo->fpL = poDS->fp_;
1832 759 : vsi_l_offset nCurOffset = VSIFTellL(poDS->fp_);
1833 759 : poDS->LoadJP2Metadata(poOpenInfo);
1834 759 : VSIFSeekL(poDS->fp_, nCurOffset, SEEK_SET);
1835 759 : poOpenInfo->fpL = nullptr;
1836 :
1837 759 : poDS->bHasGeoreferencingAtOpening =
1838 994 : (!poDS->m_oSRS.IsEmpty() || poDS->nGCPCount != 0 ||
1839 235 : poDS->bGeoTransformValid);
1840 :
1841 : /* -------------------------------------------------------------------- */
1842 : /* Vector layers */
1843 : /* -------------------------------------------------------------------- */
1844 759 : if (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
1845 : {
1846 66 : poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
1847 : "OPEN_REMOTE_GML", false));
1848 :
1849 : // If file opened in vector-only mode and there's no vector,
1850 : // return
1851 70 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
1852 4 : poDS->GetLayerCount() == 0)
1853 : {
1854 2 : delete poDS;
1855 2 : return nullptr;
1856 : }
1857 : }
1858 :
1859 : /* -------------------------------------------------------------------- */
1860 : /* Initialize any PAM information. */
1861 : /* -------------------------------------------------------------------- */
1862 757 : poDS->SetDescription(poOpenInfo->pszFilename);
1863 757 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
1864 :
1865 : /* -------------------------------------------------------------------- */
1866 : /* Check for overviews. */
1867 : /* -------------------------------------------------------------------- */
1868 757 : poDS->oOvManager.Initialize(poDS, poOpenInfo);
1869 :
1870 757 : return poDS;
1871 : }
1872 :
1873 : /************************************************************************/
1874 : /* WriteBox() */
1875 : /************************************************************************/
1876 :
1877 : template <typename CODEC, typename BASE>
1878 939 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteBox(VSILFILE *fp, GDALJP2Box *poBox)
1879 : {
1880 : GUInt32 nLBox;
1881 : GUInt32 nTBox;
1882 :
1883 939 : if (poBox == nullptr)
1884 0 : return true;
1885 :
1886 939 : nLBox = static_cast<int>(poBox->GetDataLength()) + 8;
1887 939 : nLBox = CPL_MSBWORD32(nLBox);
1888 :
1889 939 : memcpy(&nTBox, poBox->GetType(), 4);
1890 :
1891 939 : return VSIFWriteL(&nLBox, 4, 1, fp) == 1 &&
1892 1878 : VSIFWriteL(&nTBox, 4, 1, fp) == 1 &&
1893 939 : VSIFWriteL(poBox->GetWritableData(),
1894 1878 : static_cast<int>(poBox->GetDataLength()), 1, fp) == 1;
1895 : }
1896 :
1897 : /************************************************************************/
1898 : /* WriteGDALMetadataBox() */
1899 : /************************************************************************/
1900 :
1901 : template <typename CODEC, typename BASE>
1902 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteGDALMetadataBox(VSILFILE *fp,
1903 : GDALDataset *poSrcDS,
1904 : char **papszOptions)
1905 : {
1906 19 : bool bRet = true;
1907 38 : GDALJP2Box *poBox = GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
1908 19 : poSrcDS, CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false));
1909 19 : if (poBox)
1910 6 : bRet = WriteBox(fp, poBox);
1911 19 : delete poBox;
1912 19 : return bRet;
1913 : }
1914 :
1915 : /************************************************************************/
1916 : /* WriteXMLBoxes() */
1917 : /************************************************************************/
1918 :
1919 : template <typename CODEC, typename BASE>
1920 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMLBoxes(VSILFILE *fp,
1921 : GDALDataset *poSrcDS)
1922 : {
1923 19 : bool bRet = true;
1924 19 : int nBoxes = 0;
1925 19 : GDALJP2Box **papoBoxes = GDALJP2Metadata::CreateXMLBoxes(poSrcDS, &nBoxes);
1926 21 : for (int i = 0; i < nBoxes; i++)
1927 : {
1928 2 : if (!WriteBox(fp, papoBoxes[i]))
1929 0 : bRet = false;
1930 2 : delete papoBoxes[i];
1931 : }
1932 19 : CPLFree(papoBoxes);
1933 19 : return bRet;
1934 : }
1935 :
1936 : /************************************************************************/
1937 : /* WriteXMPBox() */
1938 : /************************************************************************/
1939 :
1940 : template <typename CODEC, typename BASE>
1941 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteXMPBox(VSILFILE *fp,
1942 : GDALDataset *poSrcDS)
1943 : {
1944 19 : bool bRet = true;
1945 19 : GDALJP2Box *poBox = GDALJP2Metadata::CreateXMPBox(poSrcDS);
1946 19 : if (poBox)
1947 2 : bRet = WriteBox(fp, poBox);
1948 19 : delete poBox;
1949 19 : return bRet;
1950 : }
1951 :
1952 : /************************************************************************/
1953 : /* WriteIPRBox() */
1954 : /************************************************************************/
1955 :
1956 : template <typename CODEC, typename BASE>
1957 19 : bool JP2OPJLikeDataset<CODEC, BASE>::WriteIPRBox(VSILFILE *fp,
1958 : GDALDataset *poSrcDS)
1959 : {
1960 19 : bool bRet = true;
1961 19 : GDALJP2Box *poBox = GDALJP2Metadata::CreateIPRBox(poSrcDS);
1962 19 : if (poBox)
1963 2 : bRet = WriteBox(fp, poBox);
1964 19 : delete poBox;
1965 19 : return bRet;
1966 : }
1967 :
1968 : /************************************************************************/
1969 : /* FloorPowerOfTwo() */
1970 : /************************************************************************/
1971 :
1972 542 : static int FloorPowerOfTwo(int nVal)
1973 : {
1974 542 : int nBits = 0;
1975 3791 : while (nVal > 1)
1976 : {
1977 3249 : nBits++;
1978 3249 : nVal >>= 1;
1979 : }
1980 542 : return 1 << nBits;
1981 : }
1982 :
1983 : /************************************************************************/
1984 : /* CreateCopy() */
1985 : /************************************************************************/
1986 :
1987 : template <typename CODEC, typename BASE>
1988 281 : GDALDataset *JP2OPJLikeDataset<CODEC, BASE>::CreateCopy(
1989 : const char *pszFilename, GDALDataset *poSrcDS, CPL_UNUSED int bStrict,
1990 : char **papszOptions, GDALProgressFunc pfnProgress, void *pProgressData)
1991 :
1992 : {
1993 281 : int nBands = poSrcDS->GetRasterCount();
1994 281 : int nXSize = poSrcDS->GetRasterXSize();
1995 281 : int nYSize = poSrcDS->GetRasterYSize();
1996 :
1997 281 : if (nBands == 0 || nBands > 16384)
1998 : {
1999 2 : CPLError(
2000 : CE_Failure, CPLE_NotSupported,
2001 : "Unable to export files with %d bands. Must be >= 1 and <= 16384",
2002 : nBands);
2003 2 : return nullptr;
2004 : }
2005 :
2006 279 : GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
2007 279 : if (poCT != nullptr && nBands != 1)
2008 : {
2009 1 : CPLError(CE_Failure, CPLE_NotSupported,
2010 : "JP2 driver only supports a color table for a "
2011 : "single-band dataset");
2012 1 : return nullptr;
2013 : }
2014 :
2015 278 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
2016 278 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
2017 278 : if (eDataType != GDT_Byte && eDataType != GDT_Int16 &&
2018 8 : eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
2019 : eDataType != GDT_UInt32)
2020 : {
2021 6 : CPLError(CE_Failure, CPLE_NotSupported,
2022 : "JP2 driver only supports creating Byte, GDT_Int16, "
2023 : "GDT_UInt16, GDT_Int32, GDT_UInt32");
2024 6 : return nullptr;
2025 : }
2026 :
2027 272 : const bool bInspireTG = CPLFetchBool(papszOptions, "INSPIRE_TG", false);
2028 :
2029 : /* -------------------------------------------------------------------- */
2030 : /* Analyze creation options. */
2031 : /* -------------------------------------------------------------------- */
2032 272 : auto eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
2033 272 : const char *pszCodec = CSLFetchNameValueDef(papszOptions, "CODEC", nullptr);
2034 272 : if (pszCodec)
2035 : {
2036 14 : if (EQUAL(pszCodec, "JP2"))
2037 5 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
2038 9 : else if (EQUAL(pszCodec, "J2K"))
2039 9 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_J2K);
2040 : else
2041 : {
2042 0 : CPLError(CE_Warning, CPLE_NotSupported,
2043 : "Unsupported value for CODEC : %s. Defaulting to J2K",
2044 : pszCodec);
2045 : }
2046 : }
2047 : else
2048 : {
2049 258 : if (strlen(pszFilename) > 4 &&
2050 258 : EQUAL(pszFilename + strlen(pszFilename) - 4, ".JP2"))
2051 : {
2052 223 : eCodecFormat = CODEC::cvtenum(JP2_CODEC_JP2);
2053 : }
2054 : }
2055 272 : if (eCodecFormat != CODEC::cvtenum(JP2_CODEC_JP2) && bInspireTG)
2056 : {
2057 1 : CPLError(CE_Warning, CPLE_NotSupported,
2058 : "INSPIRE_TG=YES mandates CODEC=JP2 (TG requirement 21)");
2059 1 : return nullptr;
2060 : }
2061 :
2062 : // NOTE: if changing the default block size, the logic in nitfdataset.cpp
2063 : // CreateCopy() will have to be changed as well.
2064 271 : int nBlockXSize =
2065 271 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "1024"));
2066 271 : int nBlockYSize =
2067 271 : atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "1024"));
2068 271 : if (nBlockXSize <= 0 || nBlockYSize <= 0)
2069 : {
2070 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
2071 0 : return nullptr;
2072 : }
2073 :
2074 : // By default do not generate tile sizes larger than the dataset
2075 : // dimensions
2076 542 : if (!CPLFetchBool(papszOptions, "BLOCKSIZE_STRICT", false) &&
2077 271 : !CPLFetchBool(papszOptions, "@BLOCKSIZE_STRICT", false))
2078 : {
2079 267 : if (nBlockXSize < 32 || nBlockYSize < 32)
2080 : {
2081 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid block size");
2082 0 : return nullptr;
2083 : }
2084 :
2085 267 : if (nXSize < nBlockXSize)
2086 : {
2087 242 : CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
2088 : nBlockXSize, nXSize);
2089 242 : nBlockXSize = nXSize;
2090 : }
2091 267 : if (nYSize < nBlockYSize)
2092 : {
2093 243 : CPLDebug(CODEC::debugId(), "Adjusting block width from %d to %d",
2094 : nBlockYSize, nYSize);
2095 243 : nBlockYSize = nYSize;
2096 : }
2097 : }
2098 :
2099 271 : JP2_PROG_ORDER eProgOrder = JP2_LRCP;
2100 : const char *pszPROGORDER =
2101 271 : CSLFetchNameValueDef(papszOptions, "PROGRESSION", "LRCP");
2102 271 : if (EQUAL(pszPROGORDER, "LRCP"))
2103 271 : eProgOrder = JP2_LRCP;
2104 0 : else if (EQUAL(pszPROGORDER, "RLCP"))
2105 0 : eProgOrder = JP2_RLCP;
2106 0 : else if (EQUAL(pszPROGORDER, "RPCL"))
2107 0 : eProgOrder = JP2_RPCL;
2108 0 : else if (EQUAL(pszPROGORDER, "PCRL"))
2109 0 : eProgOrder = JP2_PCRL;
2110 0 : else if (EQUAL(pszPROGORDER, "CPRL"))
2111 0 : eProgOrder = JP2_CPRL;
2112 : else
2113 : {
2114 0 : CPLError(CE_Warning, CPLE_NotSupported,
2115 : "Unsupported value for PROGRESSION : %s. Defaulting to LRCP",
2116 : pszPROGORDER);
2117 : }
2118 :
2119 271 : const bool bIsIrreversible =
2120 271 : !CPLFetchBool(papszOptions, "REVERSIBLE", poCT != nullptr);
2121 :
2122 542 : std::vector<double> adfRates;
2123 : const char *pszQuality =
2124 271 : CSLFetchNameValueDef(papszOptions, "QUALITY", nullptr);
2125 271 : double dfDefaultQuality = (poCT != nullptr) ? 100.0 : 25.0;
2126 271 : if (pszQuality)
2127 : {
2128 : char **papszTokens =
2129 41 : CSLTokenizeStringComplex(pszQuality, ",", FALSE, FALSE);
2130 158 : for (int i = 0; papszTokens[i] != nullptr; i++)
2131 : {
2132 117 : double dfQuality = CPLAtof(papszTokens[i]);
2133 117 : if (dfQuality > 0 && dfQuality <= 100)
2134 : {
2135 117 : double dfRate = 100 / dfQuality;
2136 117 : adfRates.push_back(dfRate);
2137 : }
2138 : else
2139 : {
2140 0 : CPLError(CE_Warning, CPLE_NotSupported,
2141 : "Unsupported value for QUALITY: %s. Defaulting to "
2142 : "single-layer, with quality=%.0f",
2143 0 : papszTokens[i], dfDefaultQuality);
2144 0 : adfRates.resize(0);
2145 0 : break;
2146 : }
2147 : }
2148 41 : if (papszTokens[0] == nullptr)
2149 : {
2150 0 : CPLError(CE_Warning, CPLE_NotSupported,
2151 : "Unsupported value for QUALITY: %s. Defaulting to "
2152 : "single-layer, with quality=%.0f",
2153 : pszQuality, dfDefaultQuality);
2154 : }
2155 41 : CSLDestroy(papszTokens);
2156 : }
2157 271 : if (adfRates.empty())
2158 : {
2159 230 : adfRates.push_back(100. / dfDefaultQuality);
2160 230 : assert(!adfRates.empty());
2161 : }
2162 :
2163 271 : if (poCT != nullptr && (bIsIrreversible || adfRates.back() != 1.0))
2164 : {
2165 2 : CPLError(CE_Warning, CPLE_AppDefined,
2166 : "Encoding a dataset with a color table with REVERSIBLE != YES "
2167 : "or QUALITY != 100 will likely lead to bad visual results");
2168 : }
2169 :
2170 271 : const int nMaxTileDim = std::max(nBlockXSize, nBlockYSize);
2171 271 : const int nMinTileDim = std::min(nBlockXSize, nBlockYSize);
2172 271 : int nNumResolutions = 1;
2173 : /* Pickup a reasonable value compatible with PROFILE_1 requirements */
2174 362 : while ((nMaxTileDim >> (nNumResolutions - 1)) > 128 &&
2175 92 : (nMinTileDim >> nNumResolutions) > 0)
2176 91 : nNumResolutions++;
2177 271 : int nMinProfile1Resolutions = nNumResolutions;
2178 : const char *pszResolutions =
2179 271 : CSLFetchNameValueDef(papszOptions, "RESOLUTIONS", nullptr);
2180 271 : if (pszResolutions)
2181 : {
2182 10 : nNumResolutions = atoi(pszResolutions);
2183 10 : if (nNumResolutions <= 0 || nNumResolutions >= 32 ||
2184 9 : (nMinTileDim >> nNumResolutions) == 0 ||
2185 9 : (nMaxTileDim >> nNumResolutions) == 0)
2186 : {
2187 1 : CPLError(CE_Warning, CPLE_NotSupported,
2188 : "Unsupported value for RESOLUTIONS : %s. Defaulting to %d",
2189 : pszResolutions, nMinProfile1Resolutions);
2190 1 : nNumResolutions = nMinProfile1Resolutions;
2191 : }
2192 : }
2193 271 : int nRedBandIndex = -1;
2194 271 : int nGreenBandIndex = -1;
2195 271 : int nBlueBandIndex = -1;
2196 271 : int nAlphaBandIndex = -1;
2197 606 : for (int i = 0; i < nBands; i++)
2198 : {
2199 : GDALColorInterp eInterp =
2200 335 : poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation();
2201 335 : if (eInterp == GCI_RedBand)
2202 15 : nRedBandIndex = i;
2203 320 : else if (eInterp == GCI_GreenBand)
2204 15 : nGreenBandIndex = i;
2205 305 : else if (eInterp == GCI_BlueBand)
2206 15 : nBlueBandIndex = i;
2207 290 : else if (eInterp == GCI_AlphaBand)
2208 7 : nAlphaBandIndex = i;
2209 : }
2210 271 : const char *pszAlpha = CSLFetchNameValue(papszOptions, "ALPHA");
2211 273 : if (nAlphaBandIndex < 0 && nBands > 1 && pszAlpha != nullptr &&
2212 2 : CPLTestBool(pszAlpha))
2213 : {
2214 2 : nAlphaBandIndex = nBands - 1;
2215 : }
2216 :
2217 271 : const char *pszYCBCR420 = CSLFetchNameValue(papszOptions, "YCBCR420");
2218 271 : int bYCBCR420 = FALSE;
2219 271 : if (pszYCBCR420 && CPLTestBool(pszYCBCR420))
2220 : {
2221 2 : if ((nBands == 3 || nBands == 4) && eDataType == GDT_Byte &&
2222 2 : nRedBandIndex == 0 && nGreenBandIndex == 1 && nBlueBandIndex == 2)
2223 : {
2224 2 : if (((nXSize % 2) == 0 && (nYSize % 2) == 0 &&
2225 2 : (nBlockXSize % 2) == 0 && (nBlockYSize % 2) == 0))
2226 : {
2227 2 : bYCBCR420 = TRUE;
2228 : }
2229 : else
2230 : {
2231 0 : CPLError(CE_Warning, CPLE_NotSupported,
2232 : "YCBCR420 unsupported when image size and/or tile "
2233 : "size are not multiple of 2");
2234 : }
2235 : }
2236 : else
2237 : {
2238 0 : CPLError(CE_Warning, CPLE_NotSupported,
2239 : "YCBCR420 unsupported with this image band count and/or "
2240 : "data byte");
2241 : }
2242 : }
2243 :
2244 271 : const char *pszYCC = CSLFetchNameValue(papszOptions, "YCC");
2245 292 : int bYCC = ((nBands == 3 || nBands == 4) &&
2246 21 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "YCC", "TRUE")));
2247 :
2248 271 : if (bYCBCR420 && bYCC)
2249 : {
2250 2 : if (pszYCC != nullptr)
2251 : {
2252 0 : CPLError(CE_Warning, CPLE_NotSupported,
2253 : "YCC unsupported when YCbCr requesting");
2254 : }
2255 2 : bYCC = FALSE;
2256 : }
2257 :
2258 : /* -------------------------------------------------------------------- */
2259 : /* Deal with codeblocks size */
2260 : /* -------------------------------------------------------------------- */
2261 :
2262 : int nCblockW =
2263 271 : atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_WIDTH", "64"));
2264 : int nCblockH =
2265 271 : atoi(CSLFetchNameValueDef(papszOptions, "CODEBLOCK_HEIGHT", "64"));
2266 271 : if (nCblockW < 4 || nCblockW > 1024 || nCblockH < 4 || nCblockH > 1024)
2267 : {
2268 4 : CPLError(CE_Warning, CPLE_NotSupported,
2269 : "Invalid values for codeblock size. Defaulting to 64x64");
2270 4 : nCblockW = 64;
2271 4 : nCblockH = 64;
2272 : }
2273 267 : else if (nCblockW * nCblockH > 4096)
2274 : {
2275 1 : CPLError(CE_Warning, CPLE_NotSupported,
2276 : "Invalid values for codeblock size. "
2277 : "CODEBLOCK_WIDTH * CODEBLOCK_HEIGHT should be <= 4096. "
2278 : "Defaulting to 64x64");
2279 1 : nCblockW = 64;
2280 1 : nCblockH = 64;
2281 : }
2282 271 : int nCblockW_po2 = FloorPowerOfTwo(nCblockW);
2283 271 : int nCblockH_po2 = FloorPowerOfTwo(nCblockH);
2284 271 : if (nCblockW_po2 != nCblockW || nCblockH_po2 != nCblockH)
2285 : {
2286 1 : CPLError(CE_Warning, CPLE_NotSupported,
2287 : "Non power of two values used for codeblock size. "
2288 : "Using to %dx%d",
2289 : nCblockW_po2, nCblockH_po2);
2290 : }
2291 271 : nCblockW = nCblockW_po2;
2292 271 : nCblockH = nCblockH_po2;
2293 :
2294 : /* -------------------------------------------------------------------- */
2295 : /* Deal with codestream PROFILE */
2296 : /* -------------------------------------------------------------------- */
2297 : const char *pszProfile =
2298 271 : CSLFetchNameValueDef(papszOptions, "PROFILE", "AUTO");
2299 271 : int bProfile1 = FALSE;
2300 271 : if (EQUAL(pszProfile, "UNRESTRICTED"))
2301 : {
2302 1 : bProfile1 = FALSE;
2303 1 : if (bInspireTG)
2304 : {
2305 1 : CPLError(CE_Failure, CPLE_NotSupported,
2306 : "INSPIRE_TG=YES mandates PROFILE=PROFILE_1 (TG "
2307 : "requirement 21)");
2308 1 : return nullptr;
2309 : }
2310 : }
2311 270 : else if (EQUAL(pszProfile, "UNRESTRICTED_FORCED"))
2312 : {
2313 0 : bProfile1 = FALSE;
2314 : }
2315 270 : else if (EQUAL(pszProfile,
2316 : "PROFILE_1_FORCED")) /* For debug only: can produce
2317 : inconsistent codestream */
2318 : {
2319 0 : bProfile1 = TRUE;
2320 : }
2321 : else
2322 : {
2323 270 : if (!(EQUAL(pszProfile, "PROFILE_1") || EQUAL(pszProfile, "AUTO")))
2324 : {
2325 0 : CPLError(CE_Warning, CPLE_NotSupported,
2326 : "Unsupported value for PROFILE : %s. Defaulting to AUTO",
2327 : pszProfile);
2328 0 : pszProfile = "AUTO";
2329 : }
2330 :
2331 270 : bProfile1 = TRUE;
2332 270 : const char *pszReq21OrEmpty = bInspireTG ? " (TG requirement 21)" : "";
2333 270 : if ((nBlockXSize != nXSize || nBlockYSize != nYSize) &&
2334 24 : (nBlockXSize != nBlockYSize || nBlockXSize > 1024 ||
2335 19 : nBlockYSize > 1024))
2336 : {
2337 5 : bProfile1 = FALSE;
2338 5 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2339 : {
2340 2 : CPLError(
2341 : CE_Failure, CPLE_NotSupported,
2342 : "Tile dimensions incompatible with PROFILE_1%s. "
2343 : "Should be whole image or square with dimension <= 1024.",
2344 : pszReq21OrEmpty);
2345 2 : return nullptr;
2346 : }
2347 : }
2348 268 : if ((nMaxTileDim >> (nNumResolutions - 1)) > 128)
2349 : {
2350 4 : bProfile1 = FALSE;
2351 4 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2352 : {
2353 1 : CPLError(CE_Failure, CPLE_NotSupported,
2354 : "Number of resolutions incompatible with PROFILE_1%s. "
2355 : "Should be at least %d.",
2356 : pszReq21OrEmpty, nMinProfile1Resolutions);
2357 1 : return nullptr;
2358 : }
2359 : }
2360 267 : if (nCblockW > 64 || nCblockH > 64)
2361 : {
2362 2 : bProfile1 = FALSE;
2363 2 : if (bInspireTG || EQUAL(pszProfile, "PROFILE_1"))
2364 : {
2365 2 : CPLError(CE_Failure, CPLE_NotSupported,
2366 : "Codeblock width incompatible with PROFILE_1%s. "
2367 : "Codeblock width or height should be <= 64.",
2368 : pszReq21OrEmpty);
2369 2 : return nullptr;
2370 : }
2371 : }
2372 : }
2373 :
2374 : /* -------------------------------------------------------------------- */
2375 : /* Work out the precision. */
2376 : /* -------------------------------------------------------------------- */
2377 : int nBits;
2378 265 : const int nDTBits = GDALGetDataTypeSizeBits(eDataType);
2379 :
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 = nDTBits;
2404 : }
2405 : }
2406 : else
2407 : {
2408 240 : nBits = nDTBits;
2409 : }
2410 :
2411 264 : if ((nDTBits == 8 && nBits > 8) ||
2412 264 : (nDTBits == 16 && (nBits <= 8 || nBits > 16)) ||
2413 2 : (nDTBits == 32 && (nBits <= 16 || nBits > 32)))
2414 : {
2415 0 : CPLError(CE_Warning, CPLE_NotSupported,
2416 : "Inconsistent NBITS value with data type. Using %d", nDTBits);
2417 : }
2418 :
2419 : /* -------------------------------------------------------------------- */
2420 : /* Georeferencing options */
2421 : /* -------------------------------------------------------------------- */
2422 :
2423 264 : bool bGMLJP2Option = CPLFetchBool(papszOptions, "GMLJP2", true);
2424 264 : int nGMLJP2Version = 1;
2425 : const char *pszGMLJP2V2Def =
2426 264 : CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
2427 264 : if (pszGMLJP2V2Def != nullptr)
2428 : {
2429 28 : bGMLJP2Option = true;
2430 28 : nGMLJP2Version = 2;
2431 28 : if (bInspireTG)
2432 : {
2433 0 : CPLError(CE_Warning, CPLE_NotSupported,
2434 : "INSPIRE_TG=YES is only compatible with GMLJP2 v1");
2435 0 : return nullptr;
2436 : }
2437 : }
2438 264 : const bool bGeoJP2Option = CPLFetchBool(papszOptions, "GeoJP2", true);
2439 :
2440 528 : GDALJP2Metadata oJP2MD;
2441 :
2442 264 : int bGeoreferencingCompatOfGeoJP2 = FALSE;
2443 264 : int bGeoreferencingCompatOfGMLJP2 = FALSE;
2444 270 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
2445 6 : (bGMLJP2Option || bGeoJP2Option))
2446 : {
2447 219 : if (poSrcDS->GetGCPCount() > 0)
2448 : {
2449 3 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2450 3 : oJP2MD.SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs());
2451 3 : oJP2MD.SetSpatialRef(poSrcDS->GetGCPSpatialRef());
2452 : }
2453 : else
2454 : {
2455 216 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
2456 216 : if (poSRS)
2457 : {
2458 57 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2459 57 : oJP2MD.SetSpatialRef(poSRS);
2460 : }
2461 216 : GDALGeoTransform gt;
2462 216 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
2463 : {
2464 164 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2465 164 : oJP2MD.SetGeoTransform(gt);
2466 164 : if (poSRS && !poSRS->IsEmpty())
2467 : {
2468 57 : bGeoreferencingCompatOfGMLJP2 =
2469 57 : GDALJP2Metadata::IsSRSCompatible(poSRS);
2470 57 : if (!bGeoreferencingCompatOfGMLJP2)
2471 : {
2472 1 : CPLDebug(
2473 : CODEC::debugId(),
2474 : "Cannot write GMLJP2 box due to unsupported SRS");
2475 : }
2476 : }
2477 : }
2478 : }
2479 219 : if (poSrcDS->GetMetadata("RPC") != nullptr)
2480 : {
2481 1 : oJP2MD.SetRPCMD(poSrcDS->GetMetadata("RPC"));
2482 1 : bGeoreferencingCompatOfGeoJP2 = TRUE;
2483 : }
2484 :
2485 : const char *pszAreaOrPoint =
2486 219 : poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
2487 260 : oJP2MD.bPixelIsPoint = pszAreaOrPoint != nullptr &&
2488 41 : EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
2489 :
2490 434 : if (bGMLJP2Option &&
2491 215 : CPLGetConfigOption("GMLJP2OVERRIDE", nullptr) != nullptr)
2492 : {
2493 : // Force V1 since this is the branch in which the hack is
2494 : // implemented
2495 7 : nGMLJP2Version = 1;
2496 7 : bGeoreferencingCompatOfGMLJP2 = TRUE;
2497 : }
2498 : }
2499 :
2500 264 : if (CSLFetchNameValue(papszOptions, "GMLJP2") != nullptr && bGMLJP2Option &&
2501 : !bGeoreferencingCompatOfGMLJP2)
2502 : {
2503 0 : CPLError(CE_Warning, CPLE_AppDefined,
2504 : "GMLJP2 box was explicitly required but cannot be written due "
2505 : "to lack of georeferencing and/or unsupported georeferencing "
2506 : "for GMLJP2");
2507 : }
2508 :
2509 264 : if (CSLFetchNameValue(papszOptions, "GeoJP2") != nullptr && bGeoJP2Option &&
2510 : !bGeoreferencingCompatOfGeoJP2)
2511 : {
2512 0 : CPLError(CE_Warning, CPLE_AppDefined,
2513 : "GeoJP2 box was explicitly required but cannot be written due "
2514 : "to lack of georeferencing");
2515 : }
2516 : const bool bGeoBoxesAfter =
2517 264 : CPLFetchBool(papszOptions, "GEOBOXES_AFTER_JP2C", bInspireTG);
2518 264 : GDALJP2Box *poGMLJP2Box = nullptr;
2519 264 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) && bGMLJP2Option &&
2520 : bGeoreferencingCompatOfGMLJP2)
2521 : {
2522 61 : if (nGMLJP2Version == 1)
2523 33 : poGMLJP2Box = oJP2MD.CreateGMLJP2(nXSize, nYSize);
2524 : else
2525 : poGMLJP2Box =
2526 28 : oJP2MD.CreateGMLJP2V2(nXSize, nYSize, pszGMLJP2V2Def, poSrcDS);
2527 61 : if (poGMLJP2Box == nullptr)
2528 3 : return nullptr;
2529 : }
2530 :
2531 : /* ---------------------------------------------------------------- */
2532 : /* If the input driver is identifed as "GEORASTER" the following */
2533 : /* section will try to dump a ORACLE GeoRaster JP2 BLOB into a file */
2534 : /* ---------------------------------------------------------------- */
2535 :
2536 261 : if (EQUAL(poSrcDS->GetDriverName(), "GEORASTER"))
2537 : {
2538 : const char *pszGEOR_compress =
2539 0 : poSrcDS->GetMetadataItem("COMPRESSION", "IMAGE_STRUCTURE");
2540 :
2541 0 : if (pszGEOR_compress == nullptr)
2542 : {
2543 0 : pszGEOR_compress = "NONE";
2544 : }
2545 :
2546 : /* Check if the JP2 BLOB needs re-shaping */
2547 :
2548 0 : bool bGEOR_reshape = false;
2549 :
2550 0 : const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
2551 : "BLOCKYSIZE",
2552 : "QUALITY",
2553 : "REVERSIBLE",
2554 : "RESOLUTIONS",
2555 : "PROGRESSION",
2556 : "SOP",
2557 : "EPH",
2558 : "YCBCR420",
2559 : "YCC",
2560 : "NBITS",
2561 : "1BIT_ALPHA",
2562 : "PRECINCTS",
2563 : "TILEPARTS",
2564 : "CODEBLOCK_WIDTH",
2565 : "CODEBLOCK_HEIGHT",
2566 : "PLT",
2567 : "TLM",
2568 : nullptr};
2569 :
2570 0 : for (int i = 0; apszIgnoredOptions[i]; i++)
2571 : {
2572 0 : if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
2573 : {
2574 0 : bGEOR_reshape = true;
2575 : }
2576 : }
2577 :
2578 0 : if (CSLFetchNameValue(papszOptions, "USE_SRC_CODESTREAM"))
2579 : {
2580 0 : bGEOR_reshape = false;
2581 : }
2582 :
2583 0 : char **papszGEOR_files = poSrcDS->GetFileList();
2584 :
2585 0 : if (EQUAL(pszGEOR_compress, "JP2-F") && CSLCount(papszGEOR_files) > 0 &&
2586 0 : bGEOR_reshape == false)
2587 : {
2588 :
2589 0 : const char *pszVsiOciLob = papszGEOR_files[0];
2590 :
2591 0 : VSILFILE *fpBlob = VSIFOpenL(pszVsiOciLob, "r");
2592 0 : if (fpBlob == nullptr)
2593 : {
2594 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s",
2595 : pszVsiOciLob);
2596 0 : delete poGMLJP2Box;
2597 0 : return nullptr;
2598 : }
2599 0 : VSILFILE *fp = VSIFOpenL(pszFilename, "w+b");
2600 0 : if (fp == nullptr)
2601 : {
2602 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s",
2603 : pszFilename);
2604 0 : delete poGMLJP2Box;
2605 0 : VSIFCloseL(fpBlob);
2606 0 : return nullptr;
2607 : }
2608 :
2609 0 : VSIFSeekL(fpBlob, 0, SEEK_END);
2610 :
2611 0 : size_t nBlobSize = static_cast<size_t>(VSIFTellL(fpBlob));
2612 0 : size_t nChunk = GDALGetCacheMax() / 4;
2613 0 : size_t nSize = 0;
2614 0 : size_t nCount = 0;
2615 :
2616 0 : void *pBuffer = VSI_MALLOC_VERBOSE(nChunk);
2617 0 : if (pBuffer == nullptr)
2618 : {
2619 0 : delete poGMLJP2Box;
2620 0 : VSIFCloseL(fpBlob);
2621 0 : VSIFCloseL(fp);
2622 0 : return nullptr;
2623 : }
2624 :
2625 0 : VSIFSeekL(fpBlob, 0, SEEK_SET);
2626 :
2627 0 : while ((nSize = VSIFReadL(pBuffer, 1, nChunk, fpBlob)) > 0)
2628 : {
2629 0 : VSIFWriteL(pBuffer, 1, nSize, fp);
2630 0 : nCount += nSize;
2631 0 : pfnProgress(static_cast<double>(nCount) /
2632 : static_cast<double>(nBlobSize),
2633 : nullptr, pProgressData);
2634 : }
2635 :
2636 0 : CPLFree(pBuffer);
2637 0 : VSIFCloseL(fpBlob);
2638 :
2639 0 : VSIFCloseL(fp);
2640 :
2641 : /* Return the GDALDaset object */
2642 :
2643 0 : GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
2644 0 : GDALDataset *poDS = JP2OPJLikeDataset::Open(&oOpenInfo);
2645 :
2646 : /* Copy essential metadata */
2647 :
2648 0 : GDALGeoTransform gt;
2649 0 : if (poSrcDS->GetGeoTransform(gt) == CE_None)
2650 : {
2651 0 : poDS->SetGeoTransform(gt);
2652 : }
2653 :
2654 0 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
2655 0 : if (poSRS)
2656 : {
2657 0 : poDS->SetSpatialRef(poSRS);
2658 : }
2659 :
2660 0 : delete poGMLJP2Box;
2661 0 : return poDS;
2662 : }
2663 : }
2664 :
2665 : /* -------------------------------------------------------------------- */
2666 : /* Setup encoder */
2667 : /* -------------------------------------------------------------------- */
2668 :
2669 522 : JP2OPJLikeDataset oTmpDS;
2670 261 : int numThreads = oTmpDS.GetNumThreads();
2671 :
2672 261 : CODEC localctx;
2673 261 : localctx.allocComponentParams(nBands);
2674 : int iBand;
2675 261 : int bSamePrecision = TRUE;
2676 261 : int b1BitAlpha = FALSE;
2677 586 : for (iBand = 0; iBand < nBands; iBand++)
2678 : {
2679 325 : localctx.pasBandParams[iBand].x0 = 0;
2680 325 : localctx.pasBandParams[iBand].y0 = 0;
2681 325 : if (bYCBCR420 && (iBand == 1 || iBand == 2))
2682 : {
2683 4 : localctx.pasBandParams[iBand].dx = 2;
2684 4 : localctx.pasBandParams[iBand].dy = 2;
2685 4 : localctx.pasBandParams[iBand].w = nXSize / 2;
2686 4 : localctx.pasBandParams[iBand].h = nYSize / 2;
2687 : }
2688 : else
2689 : {
2690 321 : localctx.pasBandParams[iBand].dx = 1;
2691 321 : localctx.pasBandParams[iBand].dy = 1;
2692 321 : localctx.pasBandParams[iBand].w = nXSize;
2693 321 : localctx.pasBandParams[iBand].h = nYSize;
2694 : }
2695 :
2696 325 : localctx.pasBandParams[iBand].sgnd =
2697 325 : (eDataType == GDT_Int16 || eDataType == GDT_Int32);
2698 325 : localctx.pasBandParams[iBand].prec = nBits;
2699 :
2700 : const char *pszNBits =
2701 325 : poSrcDS->GetRasterBand(iBand + 1)->GetMetadataItem(
2702 : "NBITS", "IMAGE_STRUCTURE");
2703 : /* Recommendation 38 In the case of an opacity channel, the bit depth
2704 : * should be 1-bit. */
2705 334 : if (iBand == nAlphaBandIndex &&
2706 0 : ((pszNBits != nullptr && EQUAL(pszNBits, "1")) ||
2707 9 : CPLFetchBool(papszOptions, "1BIT_ALPHA", bInspireTG)))
2708 : {
2709 3 : if (iBand != nBands - 1 && nBits != 1)
2710 : {
2711 : /* Might be a bug in openjpeg, but it seems that if the alpha */
2712 : /* band is the first one, it would select 1-bit for all
2713 : * channels... */
2714 0 : CPLError(CE_Warning, CPLE_NotSupported,
2715 : "Cannot output 1-bit alpha channel if it is not the "
2716 : "last one");
2717 : }
2718 : else
2719 : {
2720 3 : CPLDebug(CODEC::debugId(), "Using 1-bit alpha channel");
2721 3 : localctx.pasBandParams[iBand].sgnd = 0;
2722 3 : localctx.pasBandParams[iBand].prec = 1;
2723 3 : bSamePrecision = FALSE;
2724 3 : b1BitAlpha = TRUE;
2725 : }
2726 : }
2727 : }
2728 :
2729 261 : if (bInspireTG && nAlphaBandIndex >= 0 && !b1BitAlpha)
2730 : {
2731 0 : CPLError(
2732 : CE_Warning, CPLE_NotSupported,
2733 : "INSPIRE_TG=YES recommends 1BIT_ALPHA=YES (Recommendation 38)");
2734 : }
2735 261 : auto eColorSpace = CODEC::cvtenum(JP2_CLRSPC_GRAY);
2736 :
2737 261 : if (bYCBCR420)
2738 : {
2739 2 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SYCC);
2740 : }
2741 259 : else if ((nBands == 3 || nBands == 4) && nRedBandIndex >= 0 &&
2742 13 : nGreenBandIndex >= 0 && nBlueBandIndex >= 0)
2743 : {
2744 13 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
2745 : }
2746 246 : else if (poCT != nullptr)
2747 : {
2748 6 : eColorSpace = CODEC::cvtenum(JP2_CLRSPC_SRGB);
2749 : }
2750 :
2751 : /* -------------------------------------------------------------------- */
2752 : /* Create the dataset. */
2753 : /* -------------------------------------------------------------------- */
2754 :
2755 261 : const char *pszAccess =
2756 261 : STARTS_WITH_CI(pszFilename, "/vsisubfile/") ? "r+b" : "w+b";
2757 261 : VSILFILE *fp = VSIFOpenL(pszFilename, pszAccess);
2758 261 : if (fp == nullptr)
2759 : {
2760 3 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create file");
2761 3 : CPLFree(localctx.pasBandParams);
2762 3 : localctx.pasBandParams = nullptr;
2763 3 : delete poGMLJP2Box;
2764 3 : return nullptr;
2765 : }
2766 :
2767 : /* -------------------------------------------------------------------- */
2768 : /* Add JP2 boxes. */
2769 : /* -------------------------------------------------------------------- */
2770 258 : vsi_l_offset nStartJP2C = 0;
2771 258 : int bUseXLBoxes = FALSE;
2772 :
2773 258 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
2774 : {
2775 436 : GDALJP2Box jPBox(fp);
2776 218 : jPBox.SetType("jP ");
2777 218 : jPBox.AppendWritableData(4, "\x0D\x0A\x87\x0A");
2778 218 : WriteBox(fp, &jPBox);
2779 :
2780 436 : GDALJP2Box ftypBox(fp);
2781 218 : ftypBox.SetType("ftyp");
2782 : // http://docs.opengeospatial.org/is/08-085r5/08-085r5.html Req 19
2783 218 : const bool bJPXOption = CPLFetchBool(papszOptions, "JPX", true);
2784 218 : if (nGMLJP2Version == 2 && bJPXOption)
2785 25 : ftypBox.AppendWritableData(4, "jpx "); /* Branding */
2786 : else
2787 193 : ftypBox.AppendWritableData(4, "jp2 "); /* Branding */
2788 218 : ftypBox.AppendUInt32(0); /* minimum version */
2789 218 : ftypBox.AppendWritableData(
2790 : 4, "jp2 "); /* Compatibility list: first value */
2791 :
2792 218 : if (bInspireTG && poGMLJP2Box != nullptr && !bJPXOption)
2793 : {
2794 1 : CPLError(
2795 : CE_Warning, CPLE_AppDefined,
2796 : "INSPIRE_TG=YES implies following GMLJP2 specification which "
2797 : "recommends advertise reader requirement 67 feature, and thus "
2798 : "JPX capability");
2799 : }
2800 217 : else if (poGMLJP2Box != nullptr && bJPXOption)
2801 : {
2802 : /* GMLJP2 uses lbl and asoc boxes, which are JPEG2000 Part II spec
2803 : */
2804 : /* advertizing jpx is required per 8.1 of 05-047r3 GMLJP2 */
2805 57 : ftypBox.AppendWritableData(
2806 : 4, "jpx "); /* Compatibility list: second value */
2807 : }
2808 218 : WriteBox(fp, &ftypBox);
2809 :
2810 220 : const bool bIPR = poSrcDS->GetMetadata("xml:IPR") != nullptr &&
2811 2 : CPLFetchBool(papszOptions, "WRITE_METADATA", false);
2812 :
2813 : /* Reader requirement box */
2814 218 : if (poGMLJP2Box != nullptr && bJPXOption)
2815 : {
2816 114 : GDALJP2Box rreqBox(fp);
2817 57 : rreqBox.SetType("rreq");
2818 57 : rreqBox.AppendUInt8(1); /* ML = 1 byte for mask length */
2819 :
2820 57 : rreqBox.AppendUInt8(0x80 | 0x40 | (bIPR ? 0x20 : 0)); /* FUAM */
2821 57 : rreqBox.AppendUInt8(0x80); /* DCM */
2822 :
2823 57 : rreqBox.AppendUInt16(
2824 : 2 + (bIPR ? 1 : 0)); /* NSF: Number of standard features */
2825 :
2826 57 : rreqBox.AppendUInt16(
2827 : (bProfile1) ? 4 : 5); /* SF0 : PROFILE 1 or PROFILE 2 */
2828 57 : rreqBox.AppendUInt8(0x80); /* SM0 */
2829 :
2830 57 : rreqBox.AppendUInt16(67); /* SF1 : GMLJP2 box */
2831 57 : rreqBox.AppendUInt8(0x40); /* SM1 */
2832 :
2833 57 : if (bIPR)
2834 : {
2835 0 : rreqBox.AppendUInt16(35); /* SF2 : IPR metadata */
2836 0 : rreqBox.AppendUInt8(0x20); /* SM2 */
2837 : }
2838 57 : rreqBox.AppendUInt16(0); /* NVF */
2839 57 : WriteBox(fp, &rreqBox);
2840 : }
2841 :
2842 436 : GDALJP2Box ihdrBox(fp);
2843 218 : ihdrBox.SetType("ihdr");
2844 218 : ihdrBox.AppendUInt32(nYSize);
2845 218 : ihdrBox.AppendUInt32(nXSize);
2846 218 : ihdrBox.AppendUInt16(static_cast<GUInt16>(nBands));
2847 : GByte BPC;
2848 218 : if (bSamePrecision)
2849 215 : BPC = static_cast<GByte>((localctx.pasBandParams[0].prec - 1) |
2850 215 : (localctx.pasBandParams[0].sgnd << 7));
2851 : else
2852 3 : BPC = 255;
2853 218 : ihdrBox.AppendUInt8(BPC);
2854 218 : ihdrBox.AppendUInt8(7); /* C=Compression type: fixed value */
2855 218 : ihdrBox.AppendUInt8(0); /* UnkC: 0= colourspace of the image is known */
2856 : /*and correctly specified in the Colourspace Specification boxes within
2857 : * the file */
2858 218 : ihdrBox.AppendUInt8(
2859 : bIPR ? 1 : 0); /* IPR: 0=no intellectual property, 1=IPR box */
2860 :
2861 436 : GDALJP2Box bpccBox(fp);
2862 218 : if (!bSamePrecision)
2863 : {
2864 3 : bpccBox.SetType("bpcc");
2865 13 : for (int i = 0; i < nBands; i++)
2866 10 : bpccBox.AppendUInt8(
2867 10 : static_cast<GByte>((localctx.pasBandParams[i].prec - 1) |
2868 10 : (localctx.pasBandParams[i].sgnd << 7)));
2869 : }
2870 :
2871 436 : GDALJP2Box colrBox(fp);
2872 218 : colrBox.SetType("colr");
2873 218 : colrBox.AppendUInt8(1); /* METHOD: 1=Enumerated Colourspace */
2874 218 : colrBox.AppendUInt8(
2875 : 0); /* PREC: Precedence. 0=(field reserved for ISO use) */
2876 218 : colrBox.AppendUInt8(0); /* APPROX: Colourspace approximation. */
2877 218 : GUInt32 enumcs = 16;
2878 218 : if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB))
2879 16 : enumcs = 16;
2880 202 : else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY))
2881 200 : enumcs = 17;
2882 2 : else if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC))
2883 2 : enumcs = 18;
2884 218 : colrBox.AppendUInt32(enumcs); /* EnumCS: Enumerated colourspace */
2885 :
2886 436 : GDALJP2Box pclrBox(fp);
2887 436 : GDALJP2Box cmapBox(fp);
2888 218 : int nCTComponentCount = 0;
2889 218 : if (poCT != nullptr)
2890 : {
2891 6 : pclrBox.SetType("pclr");
2892 6 : const int nEntries = std::min(256, poCT->GetColorEntryCount());
2893 : nCTComponentCount =
2894 6 : atoi(CSLFetchNameValueDef(papszOptions, "CT_COMPONENTS", "0"));
2895 6 : if (bInspireTG)
2896 : {
2897 0 : if (nCTComponentCount != 0 && nCTComponentCount != 3)
2898 0 : CPLError(
2899 : CE_Warning, CPLE_AppDefined,
2900 : "Inspire TG mandates 3 components for color table");
2901 : else
2902 0 : nCTComponentCount = 3;
2903 : }
2904 6 : else if (nCTComponentCount != 3 && nCTComponentCount != 4)
2905 : {
2906 5 : nCTComponentCount = 3;
2907 21 : for (int i = 0; i < nEntries; i++)
2908 : {
2909 17 : const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
2910 17 : if (psEntry->c4 != 255)
2911 : {
2912 1 : CPLDebug(
2913 : CODEC::debugId(),
2914 : "Color table has at least one non-opaque value. "
2915 : "This may cause compatibility problems with some "
2916 : "readers. "
2917 : "In which case use CT_COMPONENTS=3 creation "
2918 : "option");
2919 1 : nCTComponentCount = 4;
2920 1 : break;
2921 : }
2922 : }
2923 : }
2924 6 : nRedBandIndex = 0;
2925 6 : nGreenBandIndex = 1;
2926 6 : nBlueBandIndex = 2;
2927 6 : nAlphaBandIndex = (nCTComponentCount == 4) ? 3 : -1;
2928 :
2929 6 : pclrBox.AppendUInt16(static_cast<GUInt16>(nEntries));
2930 6 : pclrBox.AppendUInt8(static_cast<GByte>(
2931 : nCTComponentCount)); /* NPC: Number of components */
2932 25 : for (int i = 0; i < nCTComponentCount; i++)
2933 : {
2934 19 : pclrBox.AppendUInt8(7); /* Bi: unsigned 8 bits */
2935 : }
2936 30 : for (int i = 0; i < nEntries; i++)
2937 : {
2938 24 : const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
2939 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c1));
2940 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c2));
2941 24 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c3));
2942 24 : if (nCTComponentCount == 4)
2943 4 : pclrBox.AppendUInt8(static_cast<GByte>(psEntry->c4));
2944 : }
2945 :
2946 6 : cmapBox.SetType("cmap");
2947 25 : for (int i = 0; i < nCTComponentCount; i++)
2948 : {
2949 19 : cmapBox.AppendUInt16(0); /* CMPi: code stream component index */
2950 19 : cmapBox.AppendUInt8(1); /* MYTPi: 1=palette mapping */
2951 19 : cmapBox.AppendUInt8(static_cast<GByte>(
2952 : i)); /* PCOLi: index component from the map */
2953 : }
2954 : }
2955 :
2956 436 : GDALJP2Box cdefBox(fp);
2957 228 : if (((nBands == 3 || nBands == 4) &&
2958 22 : (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
2959 18 : eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
2960 10 : (nRedBandIndex != 0 || nGreenBandIndex != 1 ||
2961 436 : nBlueBandIndex != 2)) ||
2962 : nAlphaBandIndex >= 0)
2963 : {
2964 11 : cdefBox.SetType("cdef");
2965 11 : int nComponents = (nCTComponentCount == 4) ? 4 : nBands;
2966 11 : cdefBox.AppendUInt16(static_cast<GUInt16>(nComponents));
2967 50 : for (int i = 0; i < nComponents; i++)
2968 : {
2969 39 : cdefBox.AppendUInt16(
2970 : static_cast<GUInt16>(i)); /* Component number */
2971 39 : if (i != nAlphaBandIndex)
2972 : {
2973 29 : cdefBox.AppendUInt16(
2974 : 0); /* Signification: This channel is the colour image
2975 : data for the associated colour */
2976 29 : if (eColorSpace == CODEC::cvtenum(JP2_CLRSPC_GRAY) &&
2977 : nComponents == 2)
2978 2 : cdefBox.AppendUInt16(
2979 : 1); /* Colour of the component: associated with a
2980 : particular colour */
2981 33 : else if ((eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SRGB) ||
2982 54 : eColorSpace == CODEC::cvtenum(JP2_CLRSPC_SYCC)) &&
2983 21 : (nComponents == 3 || nComponents == 4))
2984 : {
2985 24 : if (i == nRedBandIndex)
2986 8 : cdefBox.AppendUInt16(1);
2987 16 : else if (i == nGreenBandIndex)
2988 8 : cdefBox.AppendUInt16(2);
2989 8 : else if (i == nBlueBandIndex)
2990 8 : cdefBox.AppendUInt16(3);
2991 : else
2992 : {
2993 0 : CPLError(CE_Warning, CPLE_AppDefined,
2994 : "Could not associate band %d with a "
2995 : "red/green/blue channel",
2996 : i + 1);
2997 0 : cdefBox.AppendUInt16(65535);
2998 : }
2999 : }
3000 : else
3001 3 : cdefBox.AppendUInt16(
3002 : 65535); /* Colour of the component: not associated
3003 : with any particular colour */
3004 : }
3005 : else
3006 : {
3007 10 : cdefBox.AppendUInt16(
3008 : 1); /* Signification: Non pre-multiplied alpha */
3009 10 : cdefBox.AppendUInt16(
3010 : 0); /* Colour of the component: This channel is
3011 : associated as the image as a whole */
3012 : }
3013 : }
3014 : }
3015 :
3016 : // Add res box if needed
3017 218 : GDALJP2Box *poRes = nullptr;
3018 218 : if (poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION") != nullptr &&
3019 223 : poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION") != nullptr &&
3020 5 : poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT") != nullptr)
3021 : {
3022 : double dfXRes =
3023 5 : CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_XRESOLUTION"));
3024 : double dfYRes =
3025 5 : CPLAtof(poSrcDS->GetMetadataItem("TIFFTAG_YRESOLUTION"));
3026 : int nResUnit =
3027 5 : atoi(poSrcDS->GetMetadataItem("TIFFTAG_RESOLUTIONUNIT"));
3028 : #define PIXELS_PER_INCH 2
3029 : #define PIXELS_PER_CM 3
3030 :
3031 5 : if (nResUnit == PIXELS_PER_INCH)
3032 : {
3033 : // convert pixels per inch to pixels per cm.
3034 2 : dfXRes = dfXRes * 39.37 / 100.0;
3035 2 : dfYRes = dfYRes * 39.37 / 100.0;
3036 2 : nResUnit = PIXELS_PER_CM;
3037 : }
3038 :
3039 5 : if (nResUnit == PIXELS_PER_CM && dfXRes > 0 && dfYRes > 0 &&
3040 5 : dfXRes < 65535 && dfYRes < 65535)
3041 : {
3042 : /* Format a resd box and embed it inside a res box */
3043 10 : GDALJP2Box oResd;
3044 5 : oResd.SetType("resd");
3045 :
3046 5 : int nYDenom = 1;
3047 58 : while (nYDenom < 32767 && dfYRes < 32767)
3048 : {
3049 53 : dfYRes *= 2;
3050 53 : nYDenom *= 2;
3051 : }
3052 5 : int nXDenom = 1;
3053 56 : while (nXDenom < 32767 && dfXRes < 32767)
3054 : {
3055 51 : dfXRes *= 2;
3056 51 : nXDenom *= 2;
3057 : }
3058 :
3059 5 : oResd.AppendUInt16(static_cast<GUInt16>(dfYRes));
3060 5 : oResd.AppendUInt16(static_cast<GUInt16>(nYDenom));
3061 5 : oResd.AppendUInt16(static_cast<GUInt16>(dfXRes));
3062 5 : oResd.AppendUInt16(static_cast<GUInt16>(nXDenom));
3063 5 : oResd.AppendUInt8(2); /* vertical exponent */
3064 5 : oResd.AppendUInt8(2); /* horizontal exponent */
3065 :
3066 5 : GDALJP2Box *poResd = &oResd;
3067 5 : poRes = GDALJP2Box::CreateAsocBox(1, &poResd);
3068 5 : poRes->SetType("res ");
3069 : }
3070 : }
3071 :
3072 : /* Build and write jp2h super box now */
3073 : GDALJP2Box *apoBoxes[7];
3074 218 : int nBoxes = 1;
3075 218 : apoBoxes[0] = &ihdrBox;
3076 218 : if (bpccBox.GetDataLength())
3077 3 : apoBoxes[nBoxes++] = &bpccBox;
3078 218 : apoBoxes[nBoxes++] = &colrBox;
3079 218 : if (pclrBox.GetDataLength())
3080 6 : apoBoxes[nBoxes++] = &pclrBox;
3081 218 : if (cmapBox.GetDataLength())
3082 6 : apoBoxes[nBoxes++] = &cmapBox;
3083 218 : if (cdefBox.GetDataLength())
3084 11 : apoBoxes[nBoxes++] = &cdefBox;
3085 218 : if (poRes)
3086 5 : apoBoxes[nBoxes++] = poRes;
3087 : GDALJP2Box *psJP2HBox =
3088 218 : GDALJP2Box::CreateSuperBox("jp2h", nBoxes, apoBoxes);
3089 218 : WriteBox(fp, psJP2HBox);
3090 218 : delete psJP2HBox;
3091 218 : delete poRes;
3092 :
3093 218 : if (!bGeoBoxesAfter)
3094 : {
3095 207 : if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
3096 : {
3097 150 : GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
3098 150 : WriteBox(fp, poBox);
3099 150 : delete poBox;
3100 : }
3101 :
3102 218 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
3103 11 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3104 : {
3105 11 : WriteXMPBox(fp, poSrcDS);
3106 : }
3107 :
3108 207 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3109 : {
3110 11 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3111 11 : WriteXMLBoxes(fp, poSrcDS);
3112 11 : WriteGDALMetadataBox(fp, poSrcDS, papszOptions);
3113 : }
3114 :
3115 207 : if (poGMLJP2Box != nullptr)
3116 : {
3117 53 : WriteBox(fp, poGMLJP2Box);
3118 : }
3119 : }
3120 : }
3121 :
3122 : /* -------------------------------------------------------------------- */
3123 : /* Try lossless reuse of an existing JPEG2000 codestream */
3124 : /* -------------------------------------------------------------------- */
3125 258 : vsi_l_offset nCodeStreamLength = 0;
3126 258 : vsi_l_offset nCodeStreamStart = 0;
3127 258 : VSILFILE *fpSrc = nullptr;
3128 258 : if (CPLFetchBool(papszOptions, "USE_SRC_CODESTREAM", false))
3129 : {
3130 14 : CPLString osSrcFilename(poSrcDS->GetDescription());
3131 14 : if (poSrcDS->GetDriver() != nullptr &&
3132 7 : poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
3133 : {
3134 0 : VRTDataset *poVRTDS = dynamic_cast<VRTDataset *>(poSrcDS);
3135 0 : if (poVRTDS)
3136 : {
3137 : GDALDataset *poSimpleSourceDS =
3138 0 : poVRTDS->GetSingleSimpleSource();
3139 0 : if (poSimpleSourceDS)
3140 0 : osSrcFilename = poSimpleSourceDS->GetDescription();
3141 : }
3142 : }
3143 :
3144 7 : fpSrc = VSIFOpenL(osSrcFilename, "rb");
3145 7 : if (fpSrc)
3146 : {
3147 7 : nCodeStreamStart = JP2FindCodeStream(fpSrc, &nCodeStreamLength);
3148 : }
3149 7 : if (nCodeStreamLength == 0)
3150 : {
3151 1 : CPLError(
3152 : CE_Warning, CPLE_AppDefined,
3153 : "USE_SRC_CODESTREAM=YES specified, but no codestream found");
3154 : }
3155 : }
3156 :
3157 258 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2))
3158 : {
3159 : // Start codestream box
3160 218 : nStartJP2C = VSIFTellL(fp);
3161 218 : if (nCodeStreamLength)
3162 6 : bUseXLBoxes = nCodeStreamLength > UINT_MAX;
3163 : else
3164 424 : bUseXLBoxes = CPLFetchBool(papszOptions, "JP2C_XLBOX",
3165 423 : false) || /* For debugging */
3166 211 : static_cast<GIntBig>(nXSize) * nYSize * nBands *
3167 211 : nDataTypeSize / adfRates.back() >
3168 : 4e9;
3169 218 : GUInt32 nLBox = (bUseXLBoxes) ? 1 : 0;
3170 218 : CPL_MSBPTR32(&nLBox);
3171 218 : VSIFWriteL(&nLBox, 1, 4, fp);
3172 218 : VSIFWriteL("jp2c", 1, 4, fp);
3173 218 : if (bUseXLBoxes)
3174 : {
3175 1 : GUIntBig nXLBox = 0;
3176 1 : VSIFWriteL(&nXLBox, 1, 8, fp);
3177 : }
3178 : }
3179 :
3180 : /* -------------------------------------------------------------------- */
3181 : /* Do lossless reuse of an existing JPEG2000 codestream */
3182 : /* -------------------------------------------------------------------- */
3183 258 : if (fpSrc)
3184 : {
3185 7 : const char *apszIgnoredOptions[] = {"BLOCKXSIZE",
3186 : "BLOCKYSIZE",
3187 : "QUALITY",
3188 : "REVERSIBLE",
3189 : "RESOLUTIONS",
3190 : "PROGRESSION",
3191 : "SOP",
3192 : "EPH",
3193 : "YCBCR420",
3194 : "YCC",
3195 : "NBITS",
3196 : "1BIT_ALPHA",
3197 : "PRECINCTS",
3198 : "TILEPARTS",
3199 : "CODEBLOCK_WIDTH",
3200 : "CODEBLOCK_HEIGHT",
3201 : "PLT",
3202 : nullptr};
3203 126 : for (int i = 0; apszIgnoredOptions[i]; i++)
3204 : {
3205 119 : if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
3206 : {
3207 1 : CPLError(CE_Warning, CPLE_NotSupported,
3208 : "Option %s ignored when USE_SRC_CODESTREAM=YES",
3209 : apszIgnoredOptions[i]);
3210 : }
3211 : }
3212 : GByte abyBuffer[4096];
3213 7 : VSIFSeekL(fpSrc, nCodeStreamStart, SEEK_SET);
3214 7 : vsi_l_offset nRead = 0;
3215 : /* coverity[tainted_data] */
3216 17 : while (nRead < nCodeStreamLength)
3217 : {
3218 10 : const size_t nToRead =
3219 10 : (nCodeStreamLength - nRead > 4096)
3220 : ? 4096
3221 : : static_cast<size_t>(nCodeStreamLength - nRead);
3222 10 : if (VSIFReadL(abyBuffer, 1, nToRead, fpSrc) != nToRead)
3223 : {
3224 0 : VSIFCloseL(fp);
3225 0 : VSIFCloseL(fpSrc);
3226 0 : delete poGMLJP2Box;
3227 0 : return nullptr;
3228 : }
3229 10 : if (nRead == 0 && (pszProfile || bInspireTG) &&
3230 6 : abyBuffer[2] == 0xFF && abyBuffer[3] == 0x51)
3231 : {
3232 6 : if (EQUAL(pszProfile, "UNRESTRICTED"))
3233 : {
3234 0 : abyBuffer[6] = 0;
3235 0 : abyBuffer[7] = 0;
3236 : }
3237 6 : else if (EQUAL(pszProfile, "PROFILE_1") || bInspireTG)
3238 : {
3239 : // TODO: ultimately we should check that we can really set
3240 : // Profile 1
3241 1 : abyBuffer[6] = 0;
3242 1 : abyBuffer[7] = 2;
3243 : }
3244 : }
3245 20 : if (VSIFWriteL(abyBuffer, 1, nToRead, fp) != nToRead ||
3246 10 : !pfnProgress((nRead + nToRead) * 1.0 / nCodeStreamLength,
3247 : nullptr, pProgressData))
3248 : {
3249 0 : VSIFCloseL(fp);
3250 0 : VSIFCloseL(fpSrc);
3251 0 : delete poGMLJP2Box;
3252 0 : return nullptr;
3253 : }
3254 10 : nRead += nToRead;
3255 : }
3256 :
3257 7 : VSIFCloseL(fpSrc);
3258 : }
3259 : else
3260 : {
3261 251 : localctx.open(fp);
3262 251 : if (!localctx.initCompress(papszOptions, adfRates, nBlockXSize,
3263 : nBlockYSize, bIsIrreversible,
3264 : nNumResolutions, eProgOrder, bYCC, nCblockW,
3265 : nCblockH, bYCBCR420, bProfile1, nBands,
3266 : nXSize, nYSize, eColorSpace, numThreads))
3267 : {
3268 0 : CPLError(CE_Failure, CPLE_AppDefined, "init compress failed");
3269 0 : localctx.free();
3270 0 : VSIFCloseL(fp);
3271 0 : delete poGMLJP2Box;
3272 11 : return nullptr;
3273 : }
3274 251 : const int nTilesX = DIV_ROUND_UP(nXSize, nBlockXSize);
3275 251 : const int nTilesY = DIV_ROUND_UP(nYSize, nBlockYSize);
3276 :
3277 251 : const GUIntBig nTileSize = static_cast<GUIntBig>(nBlockXSize) *
3278 251 : nBlockYSize * nBands * nDataTypeSize;
3279 251 : GByte *pTempBuffer = nullptr;
3280 :
3281 251 : const bool bUseIOThread =
3282 502 : CODEC::preferPerTileCompress() && (nTilesX > 1 || nTilesY > 1) &&
3283 11 : nTileSize < 10 * 1024 * 1024 &&
3284 513 : strcmp(CPLGetThreadingModel(), "stub") != 0 &&
3285 11 : CPLTestBool(
3286 : CPLGetConfigOption("JP2OPENJPEG_USE_THREADED_IO", "YES"));
3287 :
3288 251 : if (nTileSize > UINT_MAX)
3289 : {
3290 1 : CPLError(CE_Failure, CPLE_NotSupported, "Tile size exceeds 4GB");
3291 1 : pTempBuffer = nullptr;
3292 : }
3293 : else
3294 : {
3295 : // Double memory buffer when using threaded I/O
3296 250 : const size_t nBufferSize =
3297 : static_cast<size_t>(bUseIOThread ? nTileSize * 2 : nTileSize);
3298 250 : pTempBuffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBufferSize));
3299 : }
3300 251 : if (pTempBuffer == nullptr)
3301 : {
3302 1 : localctx.free();
3303 1 : VSIFCloseL(fp);
3304 1 : delete poGMLJP2Box;
3305 1 : return nullptr;
3306 : }
3307 :
3308 250 : GByte *pYUV420Buffer = nullptr;
3309 250 : if (bYCBCR420)
3310 : {
3311 2 : pYUV420Buffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(
3312 : nBlockXSize * nBlockYSize + nBlockXSize * nBlockYSize / 2 +
3313 : ((nBands == 4) ? nBlockXSize * nBlockYSize : 0)));
3314 2 : if (pYUV420Buffer == nullptr)
3315 : {
3316 0 : localctx.free();
3317 0 : CPLFree(pTempBuffer);
3318 0 : VSIFCloseL(fp);
3319 0 : delete poGMLJP2Box;
3320 0 : return nullptr;
3321 : }
3322 : }
3323 :
3324 : /* --------------------------------------------------------------------
3325 : */
3326 : /* Iterate over the tiles */
3327 : /* --------------------------------------------------------------------
3328 : */
3329 250 : pfnProgress(0.0, nullptr, pProgressData);
3330 :
3331 : struct ReadRasterJob
3332 : {
3333 : GDALDataset *poSrcDS;
3334 : int nXOff;
3335 : int nYOff;
3336 : int nWidthToRead;
3337 : int nHeightToRead;
3338 : GDALDataType eDataType;
3339 : GByte *pBuffer;
3340 : int nBands;
3341 : CPLErr eErr;
3342 : };
3343 :
3344 812 : const auto ReadRasterFunction = [](void *threadData)
3345 : {
3346 406 : ReadRasterJob *job = static_cast<ReadRasterJob *>(threadData);
3347 812 : job->eErr = job->poSrcDS->RasterIO(
3348 : GF_Read, job->nXOff, job->nYOff, job->nWidthToRead,
3349 406 : job->nHeightToRead, job->pBuffer, job->nWidthToRead,
3350 : job->nHeightToRead, job->eDataType, job->nBands, nullptr, 0, 0,
3351 : 0, nullptr);
3352 : };
3353 :
3354 250 : CPLWorkerThreadPool oPool;
3355 250 : if (bUseIOThread)
3356 : {
3357 10 : oPool.Setup(1, nullptr, nullptr);
3358 : }
3359 :
3360 250 : GByte *pabyActiveBuffer = pTempBuffer;
3361 250 : GByte *pabyBackgroundBuffer =
3362 250 : pTempBuffer + static_cast<size_t>(nTileSize);
3363 :
3364 250 : CPLErr eErr = CE_None;
3365 250 : int iTile = 0;
3366 :
3367 : ReadRasterJob job;
3368 250 : job.eDataType = eDataType;
3369 250 : job.pBuffer = pabyActiveBuffer;
3370 250 : job.nBands = nBands;
3371 250 : job.eErr = CE_Failure;
3372 250 : job.poSrcDS = poSrcDS;
3373 :
3374 250 : if (bUseIOThread)
3375 : {
3376 10 : job.nXOff = 0;
3377 10 : job.nYOff = 0;
3378 10 : job.nWidthToRead = std::min(nBlockXSize, nXSize);
3379 10 : job.nHeightToRead = std::min(nBlockYSize, nYSize);
3380 10 : job.pBuffer = pabyBackgroundBuffer;
3381 10 : ReadRasterFunction(&job);
3382 10 : eErr = job.eErr;
3383 : }
3384 :
3385 524 : for (int nBlockYOff = 0; eErr == CE_None && nBlockYOff < nTilesY;
3386 : nBlockYOff++)
3387 : {
3388 680 : for (int nBlockXOff = 0; eErr == CE_None && nBlockXOff < nTilesX;
3389 : nBlockXOff++)
3390 : {
3391 406 : const int nWidthToRead =
3392 406 : std::min(nBlockXSize, nXSize - nBlockXOff * nBlockXSize);
3393 406 : const int nHeightToRead =
3394 406 : std::min(nBlockYSize, nYSize - nBlockYOff * nBlockYSize);
3395 :
3396 406 : if (bUseIOThread)
3397 : {
3398 : // Wait for previous background I/O task to be finished
3399 100 : oPool.WaitCompletion();
3400 100 : eErr = job.eErr;
3401 :
3402 : // Swap buffers
3403 100 : std::swap(pabyBackgroundBuffer, pabyActiveBuffer);
3404 :
3405 : // Prepare for next I/O task
3406 100 : int nNextBlockXOff = nBlockXOff + 1;
3407 100 : int nNextBlockYOff = nBlockYOff;
3408 100 : if (nNextBlockXOff == nTilesX)
3409 : {
3410 26 : nNextBlockXOff = 0;
3411 26 : nNextBlockYOff++;
3412 : }
3413 100 : if (nNextBlockYOff != nTilesY)
3414 : {
3415 90 : job.nXOff = nNextBlockXOff * nBlockXSize;
3416 90 : job.nYOff = nNextBlockYOff * nBlockYSize;
3417 90 : job.nWidthToRead =
3418 90 : std::min(nBlockXSize, nXSize - job.nXOff);
3419 90 : job.nHeightToRead =
3420 90 : std::min(nBlockYSize, nYSize - job.nYOff);
3421 90 : job.pBuffer = pabyBackgroundBuffer;
3422 :
3423 : // Submit next job
3424 90 : oPool.SubmitJob(ReadRasterFunction, &job);
3425 : }
3426 : }
3427 : else
3428 : {
3429 306 : job.nXOff = nBlockXOff * nBlockXSize;
3430 306 : job.nYOff = nBlockYOff * nBlockYSize;
3431 306 : job.nWidthToRead = nWidthToRead;
3432 306 : job.nHeightToRead = nHeightToRead;
3433 306 : ReadRasterFunction(&job);
3434 306 : eErr = job.eErr;
3435 : }
3436 :
3437 406 : if (b1BitAlpha)
3438 : {
3439 64987 : for (int i = 0; i < nWidthToRead * nHeightToRead; i++)
3440 : {
3441 64984 : if (pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3442 64984 : nHeightToRead +
3443 : i])
3444 25040 : pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3445 25040 : nHeightToRead +
3446 25040 : i] = 1;
3447 : else
3448 39944 : pabyActiveBuffer[nAlphaBandIndex * nWidthToRead *
3449 39944 : nHeightToRead +
3450 39944 : i] = 0;
3451 : }
3452 : }
3453 406 : if (eErr == CE_None)
3454 : {
3455 406 : if (bYCBCR420)
3456 : {
3457 202 : for (int j = 0; j < nHeightToRead; j++)
3458 : {
3459 27000 : for (int i = 0; i < nWidthToRead; i++)
3460 : {
3461 26800 : const int R =
3462 26800 : pabyActiveBuffer[j * nWidthToRead + i];
3463 26800 : const int G =
3464 26800 : pabyActiveBuffer[nHeightToRead *
3465 26800 : nWidthToRead +
3466 26800 : j * nWidthToRead + i];
3467 26800 : const int B =
3468 26800 : pabyActiveBuffer[2 * nHeightToRead *
3469 26800 : nWidthToRead +
3470 26800 : j * nWidthToRead + i];
3471 26800 : const int Y = static_cast<int>(
3472 26800 : 0.299 * R + 0.587 * G + 0.114 * B);
3473 53600 : const int Cb = CLAMP_0_255(static_cast<int>(
3474 26800 : -0.1687 * R - 0.3313 * G + 0.5 * B + 128));
3475 53600 : const int Cr = CLAMP_0_255(static_cast<int>(
3476 26800 : 0.5 * R - 0.4187 * G - 0.0813 * B + 128));
3477 26800 : pYUV420Buffer[j * nWidthToRead + i] =
3478 : static_cast<GByte>(Y);
3479 26800 : pYUV420Buffer[nHeightToRead * nWidthToRead +
3480 26800 : ((j / 2) * ((nWidthToRead) / 2) +
3481 26800 : i / 2)] = static_cast<GByte>(Cb);
3482 26800 : pYUV420Buffer[5 * nHeightToRead * nWidthToRead /
3483 26800 : 4 +
3484 26800 : ((j / 2) * ((nWidthToRead) / 2) +
3485 26800 : i / 2)] = static_cast<GByte>(Cr);
3486 26800 : if (nBands == 4)
3487 : {
3488 24300 : pYUV420Buffer[3 * nHeightToRead *
3489 24300 : nWidthToRead / 2 +
3490 24300 : j * nWidthToRead + i] =
3491 24300 : static_cast<GByte>(
3492 24300 : pabyActiveBuffer[3 * nHeightToRead *
3493 24300 : nWidthToRead +
3494 24300 : j * nWidthToRead +
3495 : i]);
3496 : }
3497 : }
3498 : }
3499 :
3500 2 : int nBytesToWrite =
3501 2 : 3 * nWidthToRead * nHeightToRead / 2;
3502 2 : if (nBands == 4)
3503 1 : nBytesToWrite += nBlockXSize * nBlockYSize;
3504 :
3505 2 : if (!localctx.compressTile(iTile, pYUV420Buffer,
3506 : nBytesToWrite))
3507 : {
3508 0 : CPLError(CE_Failure, CPLE_AppDefined,
3509 : "compress tile failed");
3510 0 : eErr = CE_Failure;
3511 : }
3512 : }
3513 : else
3514 : {
3515 404 : if (!localctx.compressTile(iTile, pabyActiveBuffer,
3516 404 : nWidthToRead *
3517 404 : nHeightToRead * nBands *
3518 : nDataTypeSize))
3519 : {
3520 0 : CPLError(CE_Failure, CPLE_AppDefined,
3521 : "compress tile failed");
3522 0 : eErr = CE_Failure;
3523 : }
3524 : }
3525 : }
3526 :
3527 406 : if (!pfnProgress((iTile + 1) * 1.0 / (nTilesX * nTilesY),
3528 : nullptr, pProgressData))
3529 0 : eErr = CE_Failure;
3530 :
3531 406 : iTile++;
3532 : }
3533 : }
3534 :
3535 250 : if (bUseIOThread && eErr == CE_Failure)
3536 : {
3537 : // Wait for previous background I/O task to be finished
3538 : // before freeing buffers (pTempBuffer, etc.)
3539 0 : oPool.WaitCompletion();
3540 : }
3541 :
3542 250 : VSIFree(pTempBuffer);
3543 250 : VSIFree(pYUV420Buffer);
3544 :
3545 250 : if (eErr != CE_None)
3546 : {
3547 0 : localctx.free();
3548 0 : VSIFCloseL(fp);
3549 0 : delete poGMLJP2Box;
3550 0 : return nullptr;
3551 : }
3552 :
3553 250 : if (!localctx.finishCompress())
3554 : {
3555 10 : localctx.free();
3556 10 : VSIFCloseL(fp);
3557 10 : delete poGMLJP2Box;
3558 10 : return nullptr;
3559 : }
3560 240 : localctx.free();
3561 : }
3562 :
3563 : /* -------------------------------------------------------------------- */
3564 : /* Patch JP2C box length and add trailing JP2 boxes */
3565 : /* -------------------------------------------------------------------- */
3566 247 : bool bRet = true;
3567 464 : if (eCodecFormat == CODEC::cvtenum(JP2_CODEC_JP2) &&
3568 217 : !CPLFetchBool(papszOptions, "JP2C_LENGTH_ZERO",
3569 : false) /* debug option */)
3570 : {
3571 216 : vsi_l_offset nEndJP2C = VSIFTellL(fp);
3572 216 : GUIntBig nBoxSize = nEndJP2C - nStartJP2C;
3573 216 : if (bUseXLBoxes)
3574 : {
3575 1 : VSIFSeekL(fp, nStartJP2C + 8, SEEK_SET);
3576 1 : CPL_MSBPTR64(&nBoxSize);
3577 1 : if (VSIFWriteL(&nBoxSize, 8, 1, fp) != 1)
3578 0 : bRet = false;
3579 : }
3580 : else
3581 : {
3582 215 : if (nBoxSize > UINT_MAX)
3583 : {
3584 : /* Should not happen hopefully */
3585 0 : if ((bGeoreferencingCompatOfGeoJP2 || poGMLJP2Box) &&
3586 : bGeoBoxesAfter)
3587 : {
3588 0 : CPLError(CE_Warning, CPLE_AppDefined,
3589 : "Cannot write GMLJP2/GeoJP2 boxes as codestream "
3590 : "is unexpectedly > 4GB");
3591 0 : bGeoreferencingCompatOfGeoJP2 = FALSE;
3592 0 : delete poGMLJP2Box;
3593 0 : poGMLJP2Box = nullptr;
3594 : }
3595 : }
3596 : else
3597 : {
3598 215 : VSIFSeekL(fp, nStartJP2C, SEEK_SET);
3599 215 : GUInt32 nBoxSize32 = static_cast<GUInt32>(nBoxSize);
3600 215 : CPL_MSBPTR32(&nBoxSize32);
3601 215 : if (VSIFWriteL(&nBoxSize32, 4, 1, fp) != 1)
3602 0 : bRet = false;
3603 : }
3604 : }
3605 216 : VSIFSeekL(fp, 0, SEEK_END);
3606 :
3607 216 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3608 : {
3609 14 : if (!WriteIPRBox(fp, poSrcDS))
3610 0 : bRet = false;
3611 : }
3612 :
3613 216 : if (bGeoBoxesAfter)
3614 : {
3615 11 : if (poGMLJP2Box != nullptr)
3616 : {
3617 5 : if (!WriteBox(fp, poGMLJP2Box))
3618 0 : bRet = false;
3619 : }
3620 :
3621 11 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3622 : {
3623 3 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3624 : {
3625 3 : if (!WriteXMLBoxes(fp, poSrcDS))
3626 0 : bRet = false;
3627 : }
3628 3 : if (!WriteGDALMetadataBox(fp, poSrcDS, papszOptions))
3629 0 : bRet = false;
3630 : }
3631 :
3632 11 : if (bGeoJP2Option && bGeoreferencingCompatOfGeoJP2)
3633 : {
3634 5 : GDALJP2Box *poBox = oJP2MD.CreateJP2GeoTIFF();
3635 5 : if (!WriteBox(fp, poBox))
3636 0 : bRet = false;
3637 5 : delete poBox;
3638 : }
3639 :
3640 14 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
3641 3 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
3642 : {
3643 3 : if (!WriteXMPBox(fp, poSrcDS))
3644 0 : bRet = false;
3645 : }
3646 : }
3647 : }
3648 :
3649 247 : if (VSIFCloseL(fp) != 0)
3650 0 : bRet = false;
3651 247 : delete poGMLJP2Box;
3652 247 : if (!bRet)
3653 0 : return nullptr;
3654 :
3655 : /* -------------------------------------------------------------------- */
3656 : /* Re-open dataset, and copy any auxiliary pam information. */
3657 : /* -------------------------------------------------------------------- */
3658 :
3659 247 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
3660 3 : auto poDS =
3661 247 : dynamic_cast<JP2OPJLikeDataset *>(JP2OPJLikeDataset::Open(&oOpenInfo));
3662 :
3663 247 : if (poDS)
3664 : {
3665 244 : poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT & (~GCIF_METADATA));
3666 :
3667 : /* Only write relevant metadata to PAM, and if needed */
3668 244 : if (!CPLFetchBool(papszOptions, "WRITE_METADATA", false))
3669 : {
3670 230 : char **papszSrcMD = CSLDuplicate(poSrcDS->GetMetadata());
3671 : papszSrcMD =
3672 230 : CSLSetNameValue(papszSrcMD, GDALMD_AREA_OR_POINT, nullptr);
3673 230 : papszSrcMD = CSLSetNameValue(papszSrcMD, "Corder", nullptr);
3674 321 : for (char **papszSrcMDIter = papszSrcMD;
3675 321 : papszSrcMDIter && *papszSrcMDIter;)
3676 : {
3677 : /* Remove entries like KEY= (without value) */
3678 91 : if ((*papszSrcMDIter)[0] &&
3679 91 : (*papszSrcMDIter)[strlen((*papszSrcMDIter)) - 1] == '=')
3680 : {
3681 37 : CPLFree(*papszSrcMDIter);
3682 37 : memmove(papszSrcMDIter, papszSrcMDIter + 1,
3683 : sizeof(char *) *
3684 37 : (CSLCount(papszSrcMDIter + 1) + 1));
3685 : }
3686 : else
3687 54 : ++papszSrcMDIter;
3688 : }
3689 230 : char **papszMD = CSLDuplicate(poDS->GetMetadata());
3690 230 : papszMD = CSLSetNameValue(papszMD, GDALMD_AREA_OR_POINT, nullptr);
3691 245 : if (papszSrcMD && papszSrcMD[0] != nullptr &&
3692 15 : CSLCount(papszSrcMD) != CSLCount(papszMD))
3693 : {
3694 9 : poDS->SetMetadata(papszSrcMD);
3695 : }
3696 230 : CSLDestroy(papszSrcMD);
3697 230 : CSLDestroy(papszMD);
3698 : }
3699 : }
3700 :
3701 247 : return poDS;
3702 : }
3703 :
3704 : #ifdef unused
3705 : template <typename CODEC, typename BASE>
3706 : void GDALRegisterJP2(const std::string &libraryName,
3707 : const std::string &driverName)
3708 :
3709 : {
3710 : if (!GDAL_CHECK_VERSION((driverName + " driver").c_str()))
3711 : return;
3712 :
3713 : if (GDALGetDriverByName(driverName.c_str()) != nullptr)
3714 : return;
3715 :
3716 : GDALDriver *poDriver = new GDALDriver();
3717 : poDriver->SetDescription(driverName.c_str());
3718 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
3719 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
3720 : poDriver->SetMetadataItem(
3721 : GDAL_DMD_LONGNAME,
3722 : ("JPEG-2000 driver based on " + libraryName + " library").c_str());
3723 :
3724 : poDriver->SetMetadataItem(
3725 : GDAL_DMD_HELPTOPIC,
3726 : ("drivers/raster/jp2" + CPLString(libraryName).tolower() + ".html")
3727 : .c_str());
3728 : poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/jp2");
3729 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "jp2");
3730 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "jp2 j2k");
3731 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
3732 : "Byte Int16 UInt16 Int32 UInt32");
3733 :
3734 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
3735 : BASE::setMetaData(poDriver);
3736 :
3737 : poDriver->pfnIdentify = JP2OPJLikeDataset<CODEC, BASE>::Identify;
3738 : poDriver->pfnOpen = JP2OPJLikeDataset<CODEC, BASE>::Open;
3739 : poDriver->pfnCreateCopy = JP2OPJLikeDataset<CODEC, BASE>::CreateCopy;
3740 :
3741 : GetGDALDriverManager()->RegisterDriver(poDriver);
3742 : }
3743 : #endif
|