Line data Source code
1 : /******************************************************************************
2 : *
3 : * Author: Aaron Boxer, <boxerab at protonmail dot com>
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys dot com>
7 : * Copyright (c) 2015, European Union (European Environment Agency)
8 : * Copyright (c) 2023, Grok Image Compression Inc.
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 : #pragma once
13 :
14 : #include <limits>
15 : #include <algorithm>
16 :
17 : /* This file is to be used with openjpeg 2.1 or later */
18 : #ifdef __clang__
19 : #pragma clang diagnostic push
20 : #pragma clang diagnostic ignored "-Wunknown-pragmas"
21 : #pragma clang diagnostic ignored "-Wdocumentation"
22 : #endif
23 :
24 : #include <openjpeg.h>
25 : #include <opj_config.h>
26 :
27 : #ifdef __clang__
28 : #pragma clang diagnostic pop
29 : #endif
30 :
31 : #define IS_OPENJPEG_OR_LATER(major, minor, patch) \
32 : ((OPJ_VERSION_MAJOR * 10000 + OPJ_VERSION_MINOR * 100 + \
33 : OPJ_VERSION_BUILD) >= ((major) * 10000 + (minor) * 100 + (patch)))
34 :
35 : /************************************************************************/
36 : /* JP2OpenJPEG_WarningCallback() */
37 : /************************************************************************/
38 :
39 0 : static void JP2OpenJPEG_WarningCallback(const char *pszMsg,
40 : CPL_UNUSED void *unused)
41 : {
42 0 : if (strcmp(pszMsg, "No incltree created.\n") == 0 ||
43 0 : strcmp(pszMsg, "No imsbtree created.\n") == 0 ||
44 0 : strcmp(pszMsg, "tgt_create tree->numnodes == 0, no tree created.\n") ==
45 : 0)
46 : {
47 : // Ignore warnings related to empty tag-trees. There's nothing wrong
48 : // about that.
49 : // Fixed submitted upstream with
50 : // https://github.com/uclouvain/openjpeg/pull/893
51 0 : return;
52 : }
53 0 : if (strcmp(pszMsg, "Empty SOT marker detected: Psot=12.\n") == 0)
54 : {
55 : static int bWarningEmitted = FALSE;
56 0 : if (bWarningEmitted)
57 0 : return;
58 0 : bWarningEmitted = TRUE;
59 : }
60 0 : if (strcmp(pszMsg, "JP2 box which are after the codestream will not be "
61 : "read by this function.\n") == 0)
62 : {
63 0 : return;
64 : }
65 :
66 0 : std::string osMsg(pszMsg);
67 0 : if (!osMsg.empty() && osMsg.back() == '\n')
68 0 : osMsg.resize(osMsg.size() - 1);
69 0 : CPLError(CE_Warning, CPLE_AppDefined, "%s", osMsg.c_str());
70 : }
71 :
72 : /************************************************************************/
73 : /* JP2OpenJPEG_InfoCallback() */
74 : /************************************************************************/
75 :
76 10756 : static void JP2OpenJPEG_InfoCallback(const char *pszMsg,
77 : CPL_UNUSED void *unused)
78 : {
79 21512 : std::string osMsg(pszMsg);
80 10756 : if (!osMsg.empty() && osMsg.back() == '\n')
81 10756 : osMsg.resize(osMsg.size() - 1);
82 10756 : CPLDebug("JP2OpenJPEG", "info: %s", osMsg.c_str());
83 10756 : }
84 :
85 : /************************************************************************/
86 : /* JP2OpenJPEG_ErrorCallback() */
87 : /************************************************************************/
88 :
89 6 : static void JP2OpenJPEG_ErrorCallback(const char *pszMsg,
90 : CPL_UNUSED void *unused)
91 : {
92 6 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszMsg);
93 6 : }
94 :
95 : /************************************************************************/
96 : /* JP2Dataset_Read() */
97 : /************************************************************************/
98 :
99 3758 : static size_t JP2Dataset_Read(void *pBuffer, size_t nBytes, void *pUserData)
100 : {
101 3758 : JP2File *psJP2File = static_cast<JP2File *>(pUserData);
102 : size_t nRet =
103 3758 : static_cast<size_t>(VSIFReadL(pBuffer, 1, nBytes, psJP2File->fp_));
104 : #ifdef DEBUG_IO
105 : CPLDebug(OPJCodecWrapper::debugId(),
106 : "JP2Dataset_Read(" CPL_FRMT_GUIB ") = " CPL_FRMT_GUIB,
107 : static_cast<GUIntBig>(nBytes), static_cast<GUIntBig>(nRet));
108 : #endif
109 3758 : if (nRet == 0)
110 2 : nRet = static_cast<size_t>(-1);
111 :
112 3758 : return nRet;
113 : }
114 :
115 : /************************************************************************/
116 : /* JP2Dataset_Write() */
117 : /************************************************************************/
118 :
119 251 : static size_t JP2Dataset_Write(void *pBuffer, size_t nBytes, void *pUserData)
120 : {
121 251 : JP2File *psJP2File = static_cast<JP2File *>(pUserData);
122 : size_t nRet =
123 251 : static_cast<size_t>(VSIFWriteL(pBuffer, 1, nBytes, psJP2File->fp_));
124 : #ifdef DEBUG_IO
125 : CPLDebug(OPJCodecWrapper::debugId(),
126 : "JP2Dataset_Write(" CPL_FRMT_GUIB ") = " CPL_FRMT_GUIB,
127 : static_cast<GUIntBig>(nBytes), static_cast<GUIntBig>(nRet));
128 : #endif
129 251 : if (nRet != nBytes)
130 10 : return static_cast<size_t>(-1);
131 241 : return nRet;
132 : }
133 :
134 : /************************************************************************/
135 : /* JP2Dataset_Seek() */
136 : /************************************************************************/
137 :
138 581 : static OPJ_BOOL JP2Dataset_Seek(int64_t nBytes, void *pUserData)
139 : {
140 581 : JP2File *psJP2File = static_cast<JP2File *>(pUserData);
141 : #ifdef DEBUG_IO
142 : CPLDebug(OPJCodecWrapper::debugId(), "JP2Dataset_Seek(" CPL_FRMT_GUIB ")",
143 : static_cast<GUIntBig>(nBytes));
144 : #endif
145 581 : return VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset + nBytes,
146 581 : SEEK_SET) == 0;
147 : }
148 :
149 : /************************************************************************/
150 : /* JP2Dataset_Skip() */
151 : /************************************************************************/
152 :
153 1919 : static int64_t JP2Dataset_Skip(int64_t nBytes, void *pUserData)
154 : {
155 1919 : JP2File *psJP2File = static_cast<JP2File *>(pUserData);
156 1919 : vsi_l_offset nOffset = VSIFTellL(psJP2File->fp_);
157 1919 : nOffset += nBytes;
158 : #ifdef DEBUG_IO
159 : CPLDebug(OPJCodecWrapper::debugId(),
160 : "JP2Dataset_Skip(" CPL_FRMT_GUIB " -> " CPL_FRMT_GUIB ")",
161 : static_cast<GUIntBig>(nBytes), static_cast<GUIntBig>(nOffset));
162 : #endif
163 1919 : VSIFSeekL(psJP2File->fp_, nOffset, SEEK_SET);
164 1919 : return nBytes;
165 : }
166 :
167 : /************************************************************************/
168 : /* ==================================================================== */
169 : /* OPJCodecWrapper */
170 : /* ==================================================================== */
171 : /************************************************************************/
172 :
173 : struct OPJCodecWrapper
174 : {
175 : typedef opj_codec_t jp2_codec;
176 : typedef opj_image_t jp2_image;
177 : typedef opj_stream_t jp2_stream;
178 :
179 : typedef opj_image_cmptparm_t jp2_image_comp_param;
180 : typedef opj_image_comp_t jp2_image_comp;
181 :
182 : OPJCodecWrapper() = default;
183 :
184 0 : explicit OPJCodecWrapper(OPJCodecWrapper *rhs)
185 0 : : pCodec(rhs->pCodec), pStream(rhs->pStream), psImage(rhs->psImage),
186 0 : pasBandParams(rhs->pasBandParams), psJP2File(rhs->psJP2File)
187 : {
188 0 : rhs->pCodec = nullptr;
189 0 : rhs->pStream = nullptr;
190 0 : rhs->psImage = nullptr;
191 0 : rhs->pasBandParams = nullptr;
192 0 : rhs->psJP2File = nullptr;
193 0 : }
194 :
195 2185 : ~OPJCodecWrapper(void)
196 2185 : {
197 2185 : free();
198 2185 : }
199 :
200 1157 : void open(VSILFILE *fp, vsi_l_offset offset)
201 : {
202 1157 : psJP2File = static_cast<JP2File *>(CPLMalloc(sizeof(JP2File)));
203 1157 : psJP2File->fp_ = fp;
204 1157 : psJP2File->nBaseOffset = offset;
205 1157 : }
206 :
207 252 : void open(VSILFILE *fp)
208 : {
209 252 : psJP2File = static_cast<JP2File *>(CPLMalloc(sizeof(JP2File)));
210 252 : psJP2File->fp_ = fp;
211 252 : psJP2File->nBaseOffset = VSIFTellL(fp);
212 252 : }
213 :
214 0 : void transfer(OPJCodecWrapper *rhs)
215 : {
216 0 : pCodec = rhs->pCodec;
217 0 : rhs->pCodec = nullptr;
218 0 : psImage = rhs->psImage;
219 0 : rhs->psImage = nullptr;
220 0 : psJP2File = rhs->psJP2File;
221 0 : rhs->psJP2File = nullptr;
222 0 : }
223 :
224 11250 : static int cvtenum(JP2_ENUM enumeration)
225 : {
226 11250 : switch (enumeration)
227 : {
228 1909 : case JP2_CLRSPC_UNKNOWN:
229 1909 : return OPJ_CLRSPC_UNKNOWN;
230 : break;
231 1252 : case JP2_CLRSPC_SRGB:
232 1252 : return OPJ_CLRSPC_SRGB;
233 : break;
234 2202 : case JP2_CLRSPC_GRAY:
235 2202 : return OPJ_CLRSPC_GRAY;
236 : break;
237 112 : case JP2_CLRSPC_SYCC:
238 112 : return OPJ_CLRSPC_SYCC;
239 : break;
240 1774 : case JP2_CODEC_J2K:
241 1774 : return OPJ_CODEC_J2K;
242 : break;
243 4001 : case JP2_CODEC_JP2:
244 4001 : return OPJ_CODEC_JP2;
245 : break;
246 0 : default:
247 0 : return INT_MAX;
248 : break;
249 : }
250 : }
251 :
252 35 : std::string getComment(void)
253 : {
254 : (void)this;
255 70 : std::string osComment = "Created by OpenJPEG version ";
256 :
257 70 : return osComment + opj_version();
258 : }
259 :
260 760 : void updateStrict(CPL_UNUSED bool strict)
261 : {
262 : // prevent linter from treating this as potential static method
263 : (void)this;
264 : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
265 : if (!strict)
266 : opj_decoder_set_strict_mode(pCodec, false);
267 : #endif
268 760 : }
269 :
270 20587 : static const char *debugId(void)
271 : {
272 20587 : return "OPENJPEG";
273 : }
274 :
275 262 : void allocComponentParams(int nBands)
276 : {
277 262 : pasBandParams = static_cast<jp2_image_comp_param *>(
278 262 : CPLMalloc(nBands * sizeof(jp2_image_comp_param)));
279 262 : }
280 :
281 3265 : void free(void)
282 : {
283 3265 : if (pStream)
284 1409 : opj_stream_destroy(pStream);
285 3265 : pStream = nullptr;
286 3265 : if (pCodec)
287 1409 : opj_destroy_codec(pCodec);
288 3265 : pCodec = nullptr;
289 3265 : if (psImage)
290 1405 : opj_image_destroy(psImage);
291 3265 : psImage = nullptr;
292 :
293 3265 : ::free(pasBandParams);
294 3265 : pasBandParams = nullptr;
295 :
296 3265 : CPLFree(psJP2File);
297 3265 : psJP2File = nullptr;
298 3265 : }
299 :
300 760 : static bool preferPerBlockDeCompress(void)
301 : {
302 760 : return true;
303 : }
304 :
305 332967 : static uint32_t stride(jp2_image_comp *comp)
306 : {
307 332967 : return comp->w;
308 : }
309 :
310 766 : bool setUpDecompress(CPL_UNUSED int numThreads,
311 : CPL_UNUSED char *pszFilename,
312 : vsi_l_offset nCodeStreamLength, uint32_t *nTileW,
313 : uint32_t *nTileH, int *numResolutions)
314 : {
315 :
316 766 : OPJCodecWrapper codec;
317 766 : pCodec = opj_create_decompress(static_cast<OPJ_CODEC_FORMAT>(
318 766 : OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
319 766 : if (pCodec == nullptr)
320 0 : return false;
321 :
322 766 : opj_set_info_handler(pCodec, JP2OpenJPEG_InfoCallback, nullptr);
323 766 : opj_set_warning_handler(pCodec, JP2OpenJPEG_WarningCallback, nullptr);
324 766 : opj_set_error_handler(pCodec, JP2OpenJPEG_ErrorCallback, nullptr);
325 :
326 : opj_dparameters_t decompressParams;
327 766 : opj_set_default_decoder_parameters(&decompressParams);
328 766 : if (!opj_setup_decoder(pCodec, &decompressParams))
329 : {
330 0 : opj_destroy_codec(pCodec);
331 0 : return false;
332 : }
333 :
334 766 : if (getenv("OPJ_NUM_THREADS") == nullptr)
335 : {
336 766 : opj_codec_set_threads(pCodec, numThreads);
337 : }
338 :
339 766 : pStream = CreateReadStream(psJP2File, nCodeStreamLength);
340 766 : if (pStream == nullptr)
341 : {
342 0 : CPLError(CE_Failure, CPLE_AppDefined, "CreateReadStream() failed");
343 0 : free();
344 0 : CPLFree(psJP2File);
345 0 : return false;
346 : }
347 1532 : if (VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset, SEEK_SET) == -1 ||
348 766 : !opj_read_header(pStream, pCodec, &psImage))
349 : {
350 4 : CPLError(CE_Failure, CPLE_AppDefined, "opj_read_header() failed");
351 4 : free();
352 4 : CPLFree(psJP2File);
353 4 : return false;
354 : }
355 :
356 762 : auto pCodeStreamInfo = opj_get_cstr_info(pCodec);
357 762 : *nTileW = pCodeStreamInfo->tdx;
358 762 : *nTileH = pCodeStreamInfo->tdy;
359 : #ifdef DEBUG
360 : uint32_t nX0, nY0;
361 : uint32_t nTilesX, nTilesY;
362 762 : nX0 = pCodeStreamInfo->tx0;
363 762 : nY0 = pCodeStreamInfo->ty0;
364 762 : nTilesX = pCodeStreamInfo->tw;
365 762 : nTilesY = pCodeStreamInfo->th;
366 762 : int mct = pCodeStreamInfo->m_default_tile_info.mct;
367 : #endif
368 762 : *numResolutions =
369 762 : pCodeStreamInfo->m_default_tile_info.tccp_info[0].numresolutions;
370 762 : opj_destroy_cstr_info(&pCodeStreamInfo);
371 762 : if (psImage == nullptr)
372 : {
373 0 : free();
374 0 : CPLFree(psJP2File);
375 0 : return false;
376 : }
377 : #ifdef DEBUG
378 762 : CPLDebug(OPJCodecWrapper::debugId(), "nX0 = %u", nX0);
379 762 : CPLDebug(OPJCodecWrapper::debugId(), "nY0 = %u", nY0);
380 762 : CPLDebug(OPJCodecWrapper::debugId(), "nTileW = %u", *nTileW);
381 762 : CPLDebug(OPJCodecWrapper::debugId(), "nTileH = %u", *nTileH);
382 762 : CPLDebug(OPJCodecWrapper::debugId(), "nTilesX = %u", nTilesX);
383 762 : CPLDebug(OPJCodecWrapper::debugId(), "nTilesY = %u", nTilesY);
384 762 : CPLDebug(OPJCodecWrapper::debugId(), "mct = %d", mct);
385 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->x0 = %u", psImage->x0);
386 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->y0 = %u", psImage->y0);
387 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->x1 = %u", psImage->x1);
388 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->y1 = %u", psImage->y1);
389 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->numcomps = %d",
390 762 : psImage->numcomps);
391 : // CPLDebug(OPJCodecWrapper::debugId(), "psImage->color_space = %d", psImage->color_space);
392 762 : CPLDebug(OPJCodecWrapper::debugId(), "numResolutions = %d",
393 : *numResolutions);
394 1700 : for (int i = 0; i < static_cast<int>(psImage->numcomps); i++)
395 : {
396 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].dx = %u",
397 938 : i, psImage->comps[i].dx);
398 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].dy = %u",
399 938 : i, psImage->comps[i].dy);
400 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].x0 = %u",
401 938 : i, psImage->comps[i].x0);
402 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].y0 = %u",
403 938 : i, psImage->comps[i].y0);
404 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].w = %u", i,
405 938 : psImage->comps[i].w);
406 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].h = %u", i,
407 938 : psImage->comps[i].h);
408 938 : CPLDebug(OPJCodecWrapper::debugId(),
409 : "psImage->comps[%d].resno_decoded = %d", i,
410 938 : psImage->comps[i].resno_decoded);
411 938 : CPLDebug(OPJCodecWrapper::debugId(),
412 : "psImage->comps[%d].factor = %d", i,
413 938 : psImage->comps[i].factor);
414 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].prec = %d",
415 938 : i, psImage->comps[i].prec);
416 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].sgnd = %d",
417 938 : i, psImage->comps[i].sgnd);
418 : }
419 : #endif
420 762 : if (psImage->x1 <= psImage->x0 || psImage->y1 <= psImage->y0 ||
421 762 : psImage->numcomps == 0 || (psImage->comps[0].w >> 31) != 0 ||
422 761 : (psImage->comps[0].h >> 31) != 0 || (*nTileW >> 31) != 0 ||
423 760 : (*nTileH >> 31) != 0 ||
424 760 : psImage->comps[0].w != psImage->x1 - psImage->x0 ||
425 760 : psImage->comps[0].h != psImage->y1 - psImage->y0)
426 : {
427 2 : CPLDebug(OPJCodecWrapper::debugId(),
428 : "Unable to handle that image (1)");
429 2 : free();
430 2 : CPLFree(psJP2File);
431 2 : return false;
432 : }
433 760 : return true;
434 : }
435 :
436 252 : static bool preferPerTileCompress(void)
437 : {
438 252 : return true;
439 : }
440 :
441 252 : bool initCompress(CSLConstList papszOptions,
442 : const std::vector<double> &adfRates, int nBlockXSize,
443 : int nBlockYSize, bool bIsIrreversible,
444 : int nNumResolutions, JP2_PROG_ORDER eProgOrder, int bYCC,
445 : int nCblockW, int nCblockH, int bYCBCR420, int bProfile1,
446 : int nBands, int nXSize, int nYSize,
447 : JP2_COLOR_SPACE eColorSpace,
448 : [[maybe_unused]] int numThreads)
449 : {
450 : int bSOP =
451 252 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SOP", "FALSE"));
452 : int bEPH =
453 252 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "EPH", "FALSE"));
454 :
455 : opj_cparameters_t compressParams;
456 252 : opj_set_default_encoder_parameters(&compressParams);
457 252 : if (bSOP)
458 0 : compressParams.csty |= 0x02;
459 252 : if (bEPH)
460 0 : compressParams.csty |= 0x04;
461 252 : compressParams.cp_disto_alloc = 1;
462 252 : compressParams.tcp_numlayers = static_cast<int>(adfRates.size());
463 580 : for (size_t i = 0; i < adfRates.size(); i++)
464 328 : compressParams.tcp_rates[i] = static_cast<float>(adfRates[i]);
465 252 : compressParams.cp_tx0 = 0;
466 252 : compressParams.cp_ty0 = 0;
467 252 : compressParams.tile_size_on = TRUE;
468 252 : compressParams.cp_tdx = nBlockXSize;
469 252 : compressParams.cp_tdy = nBlockYSize;
470 252 : compressParams.irreversible = bIsIrreversible;
471 252 : compressParams.numresolution = nNumResolutions;
472 252 : compressParams.prog_order = static_cast<OPJ_PROG_ORDER>(eProgOrder);
473 252 : compressParams.tcp_mct = static_cast<char>(bYCC);
474 252 : compressParams.cblockw_init = nCblockW;
475 252 : compressParams.cblockh_init = nCblockH;
476 252 : compressParams.mode = 0;
477 :
478 504 : std::string osComment;
479 252 : const char *pszCOM = CSLFetchNameValue(papszOptions, "COMMENT");
480 252 : if (pszCOM)
481 : {
482 1 : osComment = pszCOM;
483 1 : compressParams.cp_comment = &osComment[0];
484 : }
485 251 : else if (!bIsIrreversible)
486 : {
487 35 : osComment = getComment();
488 35 : if (adfRates.back() == 1.0 && !bYCBCR420)
489 : {
490 33 : osComment += ". LOSSLESS settings used";
491 : }
492 : else
493 : {
494 2 : osComment += ". LOSSY settings used";
495 : }
496 35 : compressParams.cp_comment = &osComment[0];
497 : }
498 :
499 : const char *pszCodeBlockStyle =
500 252 : CSLFetchNameValue(papszOptions, "CODEBLOCK_STYLE");
501 252 : if (pszCodeBlockStyle)
502 : {
503 4 : if (CPLGetValueType(pszCodeBlockStyle) == CPL_VALUE_INTEGER)
504 : {
505 2 : int nVal = atoi(pszCodeBlockStyle);
506 2 : if (nVal >= 0 && nVal <= 63)
507 : {
508 1 : compressParams.mode = nVal;
509 : }
510 : else
511 : {
512 1 : CPLError(CE_Warning, CPLE_NotSupported,
513 : "Invalid value for CODEBLOCK_STYLE: %s. "
514 : "Should be >= 0 and <= 63",
515 : pszCodeBlockStyle);
516 : }
517 : }
518 : else
519 : {
520 : char **papszTokens =
521 2 : CSLTokenizeString2(pszCodeBlockStyle, ", ", 0);
522 9 : for (char **papszIter = papszTokens; papszIter && *papszIter;
523 : ++papszIter)
524 : {
525 7 : if (EQUAL(*papszIter, "BYPASS"))
526 : {
527 1 : compressParams.mode |= (1 << 0);
528 : }
529 6 : else if (EQUAL(*papszIter, "RESET"))
530 : {
531 1 : compressParams.mode |= (1 << 1);
532 : }
533 5 : else if (EQUAL(*papszIter, "TERMALL"))
534 : {
535 1 : compressParams.mode |= (1 << 2);
536 : }
537 4 : else if (EQUAL(*papszIter, "VSC"))
538 : {
539 1 : compressParams.mode |= (1 << 3);
540 : }
541 3 : else if (EQUAL(*papszIter, "PREDICTABLE"))
542 : {
543 1 : compressParams.mode |= (1 << 4);
544 : }
545 2 : else if (EQUAL(*papszIter, "SEGSYM"))
546 : {
547 1 : compressParams.mode |= (1 << 5);
548 : }
549 : else
550 : {
551 1 : CPLError(CE_Warning, CPLE_NotSupported,
552 : "Unrecognized option for CODEBLOCK_STYLE: %s",
553 : *papszIter);
554 : }
555 : }
556 2 : CSLDestroy(papszTokens);
557 : }
558 : }
559 :
560 : /* Add precincts */
561 252 : const char *pszPrecincts = CSLFetchNameValueDef(
562 : papszOptions, "PRECINCTS",
563 : "{512,512},{256,512},{128,512},{64,512},{32,512},{"
564 : "16,512},{8,512},{4,512},{2,512}");
565 : char **papszTokens =
566 252 : CSLTokenizeStringComplex(pszPrecincts, "{},", FALSE, FALSE);
567 252 : int nPrecincts = CSLCount(papszTokens) / 2;
568 2484 : for (int i = 0; i < nPrecincts && i < OPJ_J2K_MAXRLVLS; i++)
569 : {
570 2232 : int nPCRW = atoi(papszTokens[2 * i]);
571 2232 : int nPCRH = atoi(papszTokens[2 * i + 1]);
572 2232 : if (nPCRW < 1 || nPCRH < 1)
573 : break;
574 2232 : compressParams.csty |= 0x01;
575 2232 : compressParams.res_spec++;
576 2232 : compressParams.prcw_init[i] = nPCRW;
577 2232 : compressParams.prch_init[i] = nPCRH;
578 : }
579 252 : CSLDestroy(papszTokens);
580 :
581 : /* Add tileparts setting */
582 : const char *pszTileParts =
583 252 : CSLFetchNameValueDef(papszOptions, "TILEPARTS", "DISABLED");
584 252 : if (EQUAL(pszTileParts, "RESOLUTIONS"))
585 : {
586 1 : compressParams.tp_on = 1;
587 1 : compressParams.tp_flag = 'R';
588 : }
589 251 : else if (EQUAL(pszTileParts, "LAYERS"))
590 : {
591 2 : if (compressParams.tcp_numlayers == 1)
592 : {
593 1 : CPLError(
594 : CE_Warning, CPLE_AppDefined,
595 : "TILEPARTS=LAYERS has no real interest with single-layer "
596 : "codestream");
597 : }
598 2 : compressParams.tp_on = 1;
599 2 : compressParams.tp_flag = 'L';
600 : }
601 249 : else if (EQUAL(pszTileParts, "COMPONENTS"))
602 : {
603 1 : compressParams.tp_on = 1;
604 1 : compressParams.tp_flag = 'C';
605 : }
606 248 : else if (!EQUAL(pszTileParts, "DISABLED"))
607 : {
608 1 : CPLError(CE_Warning, CPLE_NotSupported,
609 : "Invalid value for TILEPARTS");
610 : }
611 :
612 252 : if (bProfile1)
613 : {
614 247 : compressParams.rsiz = OPJ_PROFILE_1;
615 : }
616 :
617 : /* Always ask OpenJPEG to do codestream only. We will take care */
618 : /* of JP2 boxes */
619 252 : pCodec = opj_create_compress(static_cast<OPJ_CODEC_FORMAT>(
620 252 : OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
621 252 : if (pCodec == nullptr)
622 : {
623 0 : CPLError(CE_Failure, CPLE_AppDefined,
624 : "opj_create_compress() failed");
625 0 : return false;
626 : }
627 :
628 252 : opj_set_info_handler(pCodec, JP2OpenJPEG_InfoCallback, nullptr);
629 252 : opj_set_warning_handler(pCodec, JP2OpenJPEG_WarningCallback, nullptr);
630 252 : opj_set_error_handler(pCodec, JP2OpenJPEG_ErrorCallback, nullptr);
631 :
632 252 : psImage = opj_image_tile_create(
633 : nBands, pasBandParams, static_cast<OPJ_COLOR_SPACE>(eColorSpace));
634 :
635 252 : if (psImage == nullptr)
636 : {
637 0 : CPLError(CE_Failure, CPLE_AppDefined,
638 : "opj_image_tile_create() failed");
639 0 : free();
640 0 : return false;
641 : }
642 :
643 252 : psImage->x0 = 0;
644 252 : psImage->y0 = 0;
645 252 : psImage->x1 = nXSize;
646 252 : psImage->y1 = nYSize;
647 252 : psImage->color_space = static_cast<OPJ_COLOR_SPACE>(eColorSpace);
648 252 : psImage->numcomps = nBands;
649 :
650 252 : if (!opj_setup_encoder(pCodec, &compressParams, psImage))
651 : {
652 0 : CPLError(CE_Failure, CPLE_AppDefined, "opj_setup_encoder() failed");
653 0 : free();
654 0 : return false;
655 : }
656 :
657 : #if IS_OPENJPEG_OR_LATER(2, 4, 0)
658 : if (getenv("OPJ_NUM_THREADS") == nullptr)
659 : opj_codec_set_threads(pCodec, numThreads);
660 : CPLStringList aosOptions;
661 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "PLT", "FALSE")))
662 : {
663 : aosOptions.AddString("PLT=YES");
664 : }
665 :
666 : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
667 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "TLM", "FALSE")))
668 : {
669 : aosOptions.AddString("TLM=YES");
670 : }
671 : #endif
672 :
673 : if (!opj_encoder_set_extra_options(pCodec, aosOptions.List()))
674 : {
675 : CPLError(CE_Failure, CPLE_AppDefined,
676 : "opj_encoder_set_extra_options() failed");
677 : free();
678 : return false;
679 : }
680 : #endif
681 252 : pStream = opj_stream_create(1024 * 1024, FALSE);
682 252 : opj_stream_set_write_function(pStream, JP2Dataset_Write);
683 252 : opj_stream_set_seek_function(pStream, JP2Dataset_Seek);
684 252 : opj_stream_set_skip_function(pStream, JP2Dataset_Skip);
685 252 : opj_stream_set_user_data(pStream, psJP2File, nullptr);
686 :
687 252 : return opj_start_compress(pCodec, psImage, pStream);
688 : }
689 :
690 407 : bool compressTile(int tileIndex, GByte *buff, uint32_t buffLen)
691 : {
692 407 : if (!pCodec || !pStream)
693 0 : return false;
694 407 : return opj_write_tile(pCodec, tileIndex, buff, buffLen, pStream);
695 : }
696 :
697 251 : bool finishCompress(void)
698 : {
699 251 : bool rc = false;
700 251 : if (pCodec && pStream)
701 251 : rc = opj_end_compress(pCodec, pStream);
702 251 : if (!rc)
703 10 : CPLError(CE_Failure, CPLE_AppDefined, "opj_end_compress() failed");
704 251 : free();
705 251 : return rc;
706 : }
707 :
708 391 : void cleanUpDecompress(void)
709 : {
710 391 : if (pCodec && pStream)
711 391 : opj_end_decompress(pCodec, pStream);
712 391 : free();
713 391 : }
714 :
715 : /************************************************************************/
716 : /* CreateReadStream() */
717 : /************************************************************************/
718 1157 : static jp2_stream *CreateReadStream(JP2File *psJP2File, vsi_l_offset nSize)
719 : {
720 1157 : if (!psJP2File)
721 0 : return nullptr;
722 1157 : auto pStream = opj_stream_create(
723 : 1024, TRUE); // Default 1MB is way too big for some datasets
724 1157 : if (pStream == nullptr)
725 0 : return nullptr;
726 :
727 1157 : VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset, SEEK_SET);
728 1157 : opj_stream_set_user_data_length(pStream, nSize);
729 :
730 1157 : opj_stream_set_read_function(pStream, JP2Dataset_Read);
731 1157 : opj_stream_set_seek_function(pStream, JP2Dataset_Seek);
732 1157 : opj_stream_set_skip_function(pStream, JP2Dataset_Skip);
733 1157 : opj_stream_set_user_data(pStream, psJP2File, nullptr);
734 :
735 1157 : return pStream;
736 : }
737 :
738 : jp2_codec *pCodec = nullptr;
739 : jp2_stream *pStream = nullptr;
740 : jp2_image *psImage = nullptr;
741 : jp2_image_comp_param *pasBandParams = nullptr;
742 : JP2File *psJP2File = nullptr;
743 :
744 : OPJCodecWrapper(const OPJCodecWrapper &) = delete;
745 : OPJCodecWrapper &operator=(const OPJCodecWrapper &) = delete;
746 : };
747 :
748 : /************************************************************************/
749 : /* ==================================================================== */
750 : /* JP2OPJDatasetBase */
751 : /* ==================================================================== */
752 : /************************************************************************/
753 :
754 1905 : struct JP2OPJDatasetBase : public JP2DatasetBase
755 : {
756 : typedef opj_codec_t jp2_codec;
757 : typedef opj_image_t jp2_image;
758 : typedef opj_stream_t jp2_stream;
759 :
760 : typedef opj_image_cmptparm_t jp2_image_comp_param;
761 : typedef opj_image_comp_t jp2_image_comp;
762 :
763 : int eColorSpace = OPJCodecWrapper::cvtenum(JP2_CLRSPC_UNKNOWN);
764 : OPJCodecWrapper *m_codec = nullptr;
765 : int *m_pnLastLevel = nullptr;
766 : bool m_bStrict = true;
767 :
768 : virtual ~JP2OPJDatasetBase();
769 :
770 1905 : void init(void)
771 : {
772 : (void)this;
773 1905 : }
774 :
775 : virtual CPLErr
776 18 : AdviseRead([[maybe_unused]] int nXOff, [[maybe_unused]] int nYOff,
777 : [[maybe_unused]] int nXSize, [[maybe_unused]] int nYSize,
778 : [[maybe_unused]] int nBufXSize, [[maybe_unused]] int nBufYSize,
779 : [[maybe_unused]] GDALDataType eDT,
780 : [[maybe_unused]] int nBandCount,
781 : [[maybe_unused]] int *panBandList,
782 : [[maybe_unused]] CSLConstList papszOptions)
783 : {
784 18 : return CE_None;
785 : }
786 :
787 0 : virtual CPLErr DirectRasterIO(
788 : [[maybe_unused]] GDALRWFlag /* eRWFlag */, [[maybe_unused]] int nXOff,
789 : [[maybe_unused]] int nYOff, [[maybe_unused]] int nXSize,
790 : [[maybe_unused]] int nYSize, [[maybe_unused]] void *pData,
791 : [[maybe_unused]] int nBufXSize, [[maybe_unused]] int nBufYSize,
792 : [[maybe_unused]] GDALDataType eBufType, [[maybe_unused]] int nBandCount,
793 : [[maybe_unused]] const int *panBandMap,
794 : [[maybe_unused]] GSpacing nPixelSpace,
795 : [[maybe_unused]] GSpacing nLineSpace,
796 : [[maybe_unused]] GSpacing nBandSpace,
797 : [[maybe_unused]] GDALRasterIOExtraArg *psExtraArg)
798 :
799 : {
800 0 : return CE_None;
801 : }
802 :
803 1167 : static bool canPerformDirectIO(void)
804 : {
805 1167 : return false;
806 : }
807 :
808 391 : CPLErr readBlockInit(VSILFILE *fpIn, OPJCodecWrapper *codec, int nBlockXOff,
809 : int nBlockYOff, int nRasterXSize, int nRasterYSize,
810 : int nBlockXSize, int nBlockYSize, int nTileNumber)
811 : {
812 : const int nWidthToRead =
813 391 : std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
814 : const int nHeightToRead =
815 391 : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
816 :
817 391 : if (!codec)
818 : {
819 0 : CPLError(CE_Failure, CPLE_AppDefined, "null codec");
820 0 : return CE_Failure;
821 : }
822 :
823 391 : if (m_codec && CPLTestBool(CPLGetConfigOption(
824 : "USE_OPENJPEG_SINGLE_TILE_OPTIM", "YES")))
825 : {
826 0 : if ((*m_pnLastLevel == -1 || *m_pnLastLevel == iLevel) &&
827 0 : codec->pCodec != nullptr && *codec->pStream != nullptr &&
828 0 : m_codec->psImage != nullptr)
829 : {
830 0 : codec->transfer(m_codec);
831 : }
832 : else
833 : {
834 : // For some reason, we need to "reboot" all the machinery if
835 : // changing of overview level. Should be fixed in openjpeg
836 0 : m_codec->free();
837 : }
838 : }
839 391 : *m_pnLastLevel = iLevel;
840 :
841 391 : if (codec->pCodec == nullptr)
842 : {
843 391 : codec->pCodec = opj_create_decompress(static_cast<OPJ_CODEC_FORMAT>(
844 391 : OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
845 391 : if (codec->pCodec == nullptr)
846 : {
847 0 : CPLError(CE_Failure, CPLE_AppDefined,
848 : "opj_create_decompress() failed");
849 0 : return CE_Failure;
850 : }
851 :
852 391 : opj_set_info_handler(codec->pCodec, JP2OpenJPEG_InfoCallback,
853 : nullptr);
854 391 : opj_set_warning_handler(codec->pCodec, JP2OpenJPEG_WarningCallback,
855 : nullptr);
856 391 : opj_set_error_handler(codec->pCodec, JP2OpenJPEG_ErrorCallback,
857 : nullptr);
858 :
859 : opj_dparameters_t parameters;
860 391 : opj_set_default_decoder_parameters(¶meters);
861 391 : if (!opj_setup_decoder(codec->pCodec, ¶meters))
862 : {
863 0 : CPLError(CE_Failure, CPLE_AppDefined,
864 : "opj_setup_decoder() failed");
865 0 : return CE_Failure;
866 : }
867 : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
868 : if (!m_bStrict)
869 : {
870 : opj_decoder_set_strict_mode(codec->pCodec, false);
871 : }
872 : #endif
873 391 : if (m_codec && m_codec->psJP2File)
874 : {
875 0 : codec->pStream = OPJCodecWrapper::CreateReadStream(
876 0 : m_codec->psJP2File, nCodeStreamLength);
877 : }
878 : else
879 : {
880 391 : codec->open(fpIn, nCodeStreamStart);
881 391 : codec->pStream = OPJCodecWrapper::CreateReadStream(
882 : codec->psJP2File, nCodeStreamLength);
883 : }
884 391 : if (!codec->pStream)
885 : {
886 0 : CPLError(CE_Failure, CPLE_AppDefined,
887 : "OPJCodecWrapper::CreateReadStream() failed");
888 0 : return CE_Failure;
889 : }
890 :
891 391 : if (getenv("OPJ_NUM_THREADS") == nullptr)
892 : {
893 391 : if (m_nBlocksToLoad <= 1)
894 254 : opj_codec_set_threads(codec->pCodec, GetNumThreads());
895 : else
896 137 : opj_codec_set_threads(codec->pCodec,
897 137 : GetNumThreads() / m_nBlocksToLoad);
898 : }
899 :
900 391 : if (!opj_read_header(codec->pStream, codec->pCodec,
901 : &codec->psImage))
902 : {
903 0 : CPLError(CE_Failure, CPLE_AppDefined,
904 : "opj_read_header() failed (psImage=%p)",
905 : codec->psImage);
906 : // Hopefully the situation is better on openjpeg 2.2 regarding
907 : // cleanup
908 : // We may leak objects, but the cleanup of openjpeg can cause
909 : // double frees sometimes...
910 0 : return CE_Failure;
911 : }
912 : }
913 391 : if (!opj_set_decoded_resolution_factor(codec->pCodec, iLevel))
914 : {
915 0 : CPLError(CE_Failure, CPLE_AppDefined,
916 : "opj_set_decoded_resolution_factor() failed");
917 0 : return CE_Failure;
918 : }
919 391 : if (bUseSetDecodeArea)
920 : {
921 : /* We need to explicitly set the resolution factor on the image */
922 : /* otherwise opj_set_decode_area() will assume we decode at full */
923 : /* resolution. */
924 : /* If using parameters.cp_reduce instead of
925 : * opj_set_decoded_resolution_factor() */
926 : /* we wouldn't need to do that, as opj_read_header() would automatically
927 : */
928 : /* assign the comps[].factor to the appropriate value */
929 396 : for (unsigned int iBand = 0; iBand < codec->psImage->numcomps;
930 : iBand++)
931 : {
932 199 : codec->psImage->comps[iBand].factor = iLevel;
933 : }
934 : /* The decode area must be expressed in grid reference, ie at full*/
935 : /* scale */
936 197 : if (!opj_set_decode_area(
937 : codec->pCodec, codec->psImage,
938 197 : m_nX0 + static_cast<int>(
939 197 : static_cast<GIntBig>(nBlockXOff * nBlockXSize) *
940 197 : nParentXSize / nRasterXSize),
941 197 : m_nY0 + static_cast<int>(
942 197 : static_cast<GIntBig>(nBlockYOff * nBlockYSize) *
943 197 : nParentYSize / nRasterYSize),
944 197 : m_nX0 + static_cast<int>(
945 197 : static_cast<GIntBig>(nBlockXOff * nBlockXSize +
946 197 : nWidthToRead) *
947 197 : nParentXSize / nRasterXSize),
948 197 : m_nY0 + static_cast<int>(
949 197 : static_cast<GIntBig>(nBlockYOff * nBlockYSize +
950 197 : nHeightToRead) *
951 197 : nParentYSize / nRasterYSize)))
952 : {
953 0 : CPLError(CE_Failure, CPLE_AppDefined,
954 : "opj_set_decode_area() failed");
955 0 : return CE_Failure;
956 : }
957 197 : if (!opj_decode(codec->pCodec, codec->pStream, codec->psImage))
958 : {
959 0 : CPLError(CE_Failure, CPLE_AppDefined, "opj_decode() failed");
960 0 : return CE_Failure;
961 : }
962 : }
963 : else
964 : {
965 194 : if (!opj_get_decoded_tile(codec->pCodec, codec->pStream,
966 : codec->psImage, nTileNumber))
967 : {
968 0 : CPLError(CE_Failure, CPLE_AppDefined,
969 : "opj_get_decoded_tile() failed");
970 0 : return CE_Failure;
971 : }
972 : }
973 :
974 391 : return CE_None;
975 : }
976 :
977 32 : void cache(CPL_UNUSED JP2OPJDatasetBase *rhs)
978 : {
979 : // prevent linter from treating this as potential static method
980 : (void)this;
981 32 : if (m_codec && rhs)
982 0 : m_codec->transfer(rhs->m_codec);
983 32 : }
984 :
985 580 : void cacheNew(CPL_UNUSED OPJCodecWrapper *codec)
986 : {
987 : // prevent linter from treating this as potential static method
988 : (void)this;
989 580 : if (!codec)
990 0 : return;
991 580 : if (m_codec)
992 0 : m_codec = new OPJCodecWrapper(codec);
993 : }
994 :
995 391 : void cache(CPL_UNUSED OPJCodecWrapper *codec)
996 : {
997 : // prevent linter from treating this as potential static method
998 : (void)this;
999 391 : if (!codec)
1000 0 : return;
1001 :
1002 391 : if (m_codec && CPLTestBool(CPLGetConfigOption(
1003 : "USE_OPENJPEG_SINGLE_TILE_OPTIM", "YES")))
1004 : {
1005 0 : codec->transfer(m_codec);
1006 : }
1007 : else
1008 : {
1009 391 : codec->cleanUpDecompress();
1010 : }
1011 : }
1012 :
1013 760 : void openCompleteJP2(OPJCodecWrapper *codec)
1014 : {
1015 : // prevent linter from treating this as potential static method
1016 : (void)this;
1017 760 : if (bSingleTiled && bUseSetDecodeArea)
1018 : {
1019 : // nothing
1020 : }
1021 : else
1022 : {
1023 180 : if (codec)
1024 180 : codec->free();
1025 : }
1026 760 : }
1027 :
1028 1905 : void closeJP2(void)
1029 : {
1030 : // prevent linter from treating this as potential static method
1031 : (void)this;
1032 1905 : if (iLevel == 0)
1033 : {
1034 1788 : if (m_codec)
1035 0 : m_codec->free();
1036 1788 : delete m_pnLastLevel;
1037 1788 : m_pnLastLevel = nullptr;
1038 : }
1039 1905 : }
1040 : };
|