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