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 12770 : static int cvtenum(JP2_ENUM enumeration)
225 : {
226 12770 : 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 5521 : case JP2_CODEC_JP2:
244 5521 : 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 611 : static uint32_t stride(jp2_image_comp *comp)
306 : {
307 611 : return comp->w;
308 : }
309 :
310 449 : static GDALDataType getDataType(CPL_UNUSED jp2_image_comp *comp)
311 : {
312 449 : return GDT_Int32;
313 : }
314 :
315 766 : bool setUpDecompress(CPL_UNUSED int numThreads,
316 : CPL_UNUSED char *pszFilename,
317 : vsi_l_offset nCodeStreamLength, uint32_t *nTileW,
318 : uint32_t *nTileH, int *numResolutions)
319 : {
320 :
321 766 : OPJCodecWrapper codec;
322 766 : pCodec = opj_create_decompress(static_cast<OPJ_CODEC_FORMAT>(
323 766 : OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
324 766 : if (pCodec == nullptr)
325 0 : return false;
326 :
327 766 : opj_set_info_handler(pCodec, JP2OpenJPEG_InfoCallback, nullptr);
328 766 : opj_set_warning_handler(pCodec, JP2OpenJPEG_WarningCallback, nullptr);
329 766 : opj_set_error_handler(pCodec, JP2OpenJPEG_ErrorCallback, nullptr);
330 :
331 : opj_dparameters_t decompressParams;
332 766 : opj_set_default_decoder_parameters(&decompressParams);
333 766 : if (!opj_setup_decoder(pCodec, &decompressParams))
334 : {
335 0 : opj_destroy_codec(pCodec);
336 0 : return false;
337 : }
338 :
339 766 : if (getenv("OPJ_NUM_THREADS") == nullptr)
340 : {
341 766 : opj_codec_set_threads(pCodec, numThreads);
342 : }
343 :
344 766 : pStream = CreateReadStream(psJP2File, nCodeStreamLength);
345 766 : if (pStream == nullptr)
346 : {
347 0 : CPLError(CE_Failure, CPLE_AppDefined, "CreateReadStream() failed");
348 0 : free();
349 0 : CPLFree(psJP2File);
350 0 : return false;
351 : }
352 1532 : if (VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset, SEEK_SET) == -1 ||
353 766 : !opj_read_header(pStream, pCodec, &psImage))
354 : {
355 4 : CPLError(CE_Failure, CPLE_AppDefined, "opj_read_header() failed");
356 4 : free();
357 4 : CPLFree(psJP2File);
358 4 : return false;
359 : }
360 :
361 762 : auto pCodeStreamInfo = opj_get_cstr_info(pCodec);
362 762 : *nTileW = pCodeStreamInfo->tdx;
363 762 : *nTileH = pCodeStreamInfo->tdy;
364 : #ifdef DEBUG
365 : uint32_t nX0, nY0;
366 : uint32_t nTilesX, nTilesY;
367 762 : nX0 = pCodeStreamInfo->tx0;
368 762 : nY0 = pCodeStreamInfo->ty0;
369 762 : nTilesX = pCodeStreamInfo->tw;
370 762 : nTilesY = pCodeStreamInfo->th;
371 762 : int mct = pCodeStreamInfo->m_default_tile_info.mct;
372 : #endif
373 762 : *numResolutions =
374 762 : pCodeStreamInfo->m_default_tile_info.tccp_info[0].numresolutions;
375 762 : opj_destroy_cstr_info(&pCodeStreamInfo);
376 762 : if (psImage == nullptr)
377 : {
378 0 : free();
379 0 : CPLFree(psJP2File);
380 0 : return false;
381 : }
382 : #ifdef DEBUG
383 762 : CPLDebug(OPJCodecWrapper::debugId(), "nX0 = %u", nX0);
384 762 : CPLDebug(OPJCodecWrapper::debugId(), "nY0 = %u", nY0);
385 762 : CPLDebug(OPJCodecWrapper::debugId(), "nTileW = %u", *nTileW);
386 762 : CPLDebug(OPJCodecWrapper::debugId(), "nTileH = %u", *nTileH);
387 762 : CPLDebug(OPJCodecWrapper::debugId(), "nTilesX = %u", nTilesX);
388 762 : CPLDebug(OPJCodecWrapper::debugId(), "nTilesY = %u", nTilesY);
389 762 : CPLDebug(OPJCodecWrapper::debugId(), "mct = %d", mct);
390 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->x0 = %u", psImage->x0);
391 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->y0 = %u", psImage->y0);
392 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->x1 = %u", psImage->x1);
393 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->y1 = %u", psImage->y1);
394 762 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->numcomps = %d",
395 762 : psImage->numcomps);
396 : // CPLDebug(OPJCodecWrapper::debugId(), "psImage->color_space = %d", psImage->color_space);
397 762 : CPLDebug(OPJCodecWrapper::debugId(), "numResolutions = %d",
398 : *numResolutions);
399 1700 : for (int i = 0; i < static_cast<int>(psImage->numcomps); i++)
400 : {
401 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].dx = %u",
402 938 : i, psImage->comps[i].dx);
403 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].dy = %u",
404 938 : i, psImage->comps[i].dy);
405 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].x0 = %u",
406 938 : i, psImage->comps[i].x0);
407 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].y0 = %u",
408 938 : i, psImage->comps[i].y0);
409 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].w = %u", i,
410 938 : psImage->comps[i].w);
411 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].h = %u", i,
412 938 : psImage->comps[i].h);
413 938 : CPLDebug(OPJCodecWrapper::debugId(),
414 : "psImage->comps[%d].resno_decoded = %d", i,
415 938 : psImage->comps[i].resno_decoded);
416 938 : CPLDebug(OPJCodecWrapper::debugId(),
417 : "psImage->comps[%d].factor = %d", i,
418 938 : psImage->comps[i].factor);
419 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].prec = %d",
420 938 : i, psImage->comps[i].prec);
421 938 : CPLDebug(OPJCodecWrapper::debugId(), "psImage->comps[%d].sgnd = %d",
422 938 : i, psImage->comps[i].sgnd);
423 : }
424 : #endif
425 762 : if (psImage->x1 <= psImage->x0 || psImage->y1 <= psImage->y0 ||
426 762 : psImage->numcomps == 0 || (psImage->comps[0].w >> 31) != 0 ||
427 761 : (psImage->comps[0].h >> 31) != 0 || (*nTileW >> 31) != 0 ||
428 760 : (*nTileH >> 31) != 0 ||
429 760 : psImage->comps[0].w != psImage->x1 - psImage->x0 ||
430 760 : psImage->comps[0].h != psImage->y1 - psImage->y0)
431 : {
432 2 : CPLDebug(OPJCodecWrapper::debugId(),
433 : "Unable to handle that image (1)");
434 2 : free();
435 2 : CPLFree(psJP2File);
436 2 : return false;
437 : }
438 760 : return true;
439 : }
440 :
441 252 : static bool preferPerTileCompress(void)
442 : {
443 252 : return true;
444 : }
445 :
446 252 : bool initCompress(CSLConstList papszOptions,
447 : const std::vector<double> &adfRates, int nBlockXSize,
448 : int nBlockYSize, bool bIsIrreversible,
449 : int nNumResolutions, JP2_PROG_ORDER eProgOrder, int bYCC,
450 : int nCblockW, int nCblockH, int bYCBCR420, int bProfile1,
451 : int nBands, int nXSize, int nYSize,
452 : JP2_COLOR_SPACE eColorSpace,
453 : [[maybe_unused]] int numThreads)
454 : {
455 : int bSOP =
456 252 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SOP", "FALSE"));
457 : int bEPH =
458 252 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "EPH", "FALSE"));
459 :
460 : opj_cparameters_t compressParams;
461 252 : opj_set_default_encoder_parameters(&compressParams);
462 252 : if (bSOP)
463 0 : compressParams.csty |= 0x02;
464 252 : if (bEPH)
465 0 : compressParams.csty |= 0x04;
466 252 : compressParams.cp_disto_alloc = 1;
467 252 : compressParams.tcp_numlayers = static_cast<int>(adfRates.size());
468 580 : for (size_t i = 0; i < adfRates.size(); i++)
469 328 : compressParams.tcp_rates[i] = static_cast<float>(adfRates[i]);
470 252 : compressParams.cp_tx0 = 0;
471 252 : compressParams.cp_ty0 = 0;
472 252 : compressParams.tile_size_on = TRUE;
473 252 : compressParams.cp_tdx = nBlockXSize;
474 252 : compressParams.cp_tdy = nBlockYSize;
475 252 : compressParams.irreversible = bIsIrreversible;
476 252 : compressParams.numresolution = nNumResolutions;
477 252 : compressParams.prog_order = static_cast<OPJ_PROG_ORDER>(eProgOrder);
478 252 : compressParams.tcp_mct = static_cast<char>(bYCC);
479 252 : compressParams.cblockw_init = nCblockW;
480 252 : compressParams.cblockh_init = nCblockH;
481 252 : compressParams.mode = 0;
482 :
483 504 : std::string osComment;
484 252 : const char *pszCOM = CSLFetchNameValue(papszOptions, "COMMENT");
485 252 : if (pszCOM)
486 : {
487 1 : osComment = pszCOM;
488 1 : compressParams.cp_comment = &osComment[0];
489 : }
490 251 : else if (!bIsIrreversible)
491 : {
492 35 : osComment = getComment();
493 35 : if (adfRates.back() == 1.0 && !bYCBCR420)
494 : {
495 33 : osComment += ". LOSSLESS settings used";
496 : }
497 : else
498 : {
499 2 : osComment += ". LOSSY settings used";
500 : }
501 35 : compressParams.cp_comment = &osComment[0];
502 : }
503 :
504 : const char *pszCodeBlockStyle =
505 252 : CSLFetchNameValue(papszOptions, "CODEBLOCK_STYLE");
506 252 : if (pszCodeBlockStyle)
507 : {
508 4 : if (CPLGetValueType(pszCodeBlockStyle) == CPL_VALUE_INTEGER)
509 : {
510 2 : int nVal = atoi(pszCodeBlockStyle);
511 2 : if (nVal >= 0 && nVal <= 63)
512 : {
513 1 : compressParams.mode = nVal;
514 : }
515 : else
516 : {
517 1 : CPLError(CE_Warning, CPLE_NotSupported,
518 : "Invalid value for CODEBLOCK_STYLE: %s. "
519 : "Should be >= 0 and <= 63",
520 : pszCodeBlockStyle);
521 : }
522 : }
523 : else
524 : {
525 : char **papszTokens =
526 2 : CSLTokenizeString2(pszCodeBlockStyle, ", ", 0);
527 9 : for (char **papszIter = papszTokens; papszIter && *papszIter;
528 : ++papszIter)
529 : {
530 7 : if (EQUAL(*papszIter, "BYPASS"))
531 : {
532 1 : compressParams.mode |= (1 << 0);
533 : }
534 6 : else if (EQUAL(*papszIter, "RESET"))
535 : {
536 1 : compressParams.mode |= (1 << 1);
537 : }
538 5 : else if (EQUAL(*papszIter, "TERMALL"))
539 : {
540 1 : compressParams.mode |= (1 << 2);
541 : }
542 4 : else if (EQUAL(*papszIter, "VSC"))
543 : {
544 1 : compressParams.mode |= (1 << 3);
545 : }
546 3 : else if (EQUAL(*papszIter, "PREDICTABLE"))
547 : {
548 1 : compressParams.mode |= (1 << 4);
549 : }
550 2 : else if (EQUAL(*papszIter, "SEGSYM"))
551 : {
552 1 : compressParams.mode |= (1 << 5);
553 : }
554 : else
555 : {
556 1 : CPLError(CE_Warning, CPLE_NotSupported,
557 : "Unrecognized option for CODEBLOCK_STYLE: %s",
558 : *papszIter);
559 : }
560 : }
561 2 : CSLDestroy(papszTokens);
562 : }
563 : }
564 :
565 : /* Add precincts */
566 252 : const char *pszPrecincts = CSLFetchNameValueDef(
567 : papszOptions, "PRECINCTS",
568 : "{512,512},{256,512},{128,512},{64,512},{32,512},{"
569 : "16,512},{8,512},{4,512},{2,512}");
570 : char **papszTokens =
571 252 : CSLTokenizeStringComplex(pszPrecincts, "{},", FALSE, FALSE);
572 252 : int nPrecincts = CSLCount(papszTokens) / 2;
573 2484 : for (int i = 0; i < nPrecincts && i < OPJ_J2K_MAXRLVLS; i++)
574 : {
575 2232 : int nPCRW = atoi(papszTokens[2 * i]);
576 2232 : int nPCRH = atoi(papszTokens[2 * i + 1]);
577 2232 : if (nPCRW < 1 || nPCRH < 1)
578 : break;
579 2232 : compressParams.csty |= 0x01;
580 2232 : compressParams.res_spec++;
581 2232 : compressParams.prcw_init[i] = nPCRW;
582 2232 : compressParams.prch_init[i] = nPCRH;
583 : }
584 252 : CSLDestroy(papszTokens);
585 :
586 : /* Add tileparts setting */
587 : const char *pszTileParts =
588 252 : CSLFetchNameValueDef(papszOptions, "TILEPARTS", "DISABLED");
589 252 : if (EQUAL(pszTileParts, "RESOLUTIONS"))
590 : {
591 1 : compressParams.tp_on = 1;
592 1 : compressParams.tp_flag = 'R';
593 : }
594 251 : else if (EQUAL(pszTileParts, "LAYERS"))
595 : {
596 2 : if (compressParams.tcp_numlayers == 1)
597 : {
598 1 : CPLError(
599 : CE_Warning, CPLE_AppDefined,
600 : "TILEPARTS=LAYERS has no real interest with single-layer "
601 : "codestream");
602 : }
603 2 : compressParams.tp_on = 1;
604 2 : compressParams.tp_flag = 'L';
605 : }
606 249 : else if (EQUAL(pszTileParts, "COMPONENTS"))
607 : {
608 1 : compressParams.tp_on = 1;
609 1 : compressParams.tp_flag = 'C';
610 : }
611 248 : else if (!EQUAL(pszTileParts, "DISABLED"))
612 : {
613 1 : CPLError(CE_Warning, CPLE_NotSupported,
614 : "Invalid value for TILEPARTS");
615 : }
616 :
617 252 : if (bProfile1)
618 : {
619 247 : compressParams.rsiz = OPJ_PROFILE_1;
620 : }
621 :
622 : /* Always ask OpenJPEG to do codestream only. We will take care */
623 : /* of JP2 boxes */
624 252 : pCodec = opj_create_compress(static_cast<OPJ_CODEC_FORMAT>(
625 252 : OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
626 252 : if (pCodec == nullptr)
627 : {
628 0 : CPLError(CE_Failure, CPLE_AppDefined,
629 : "opj_create_compress() failed");
630 0 : return false;
631 : }
632 :
633 252 : opj_set_info_handler(pCodec, JP2OpenJPEG_InfoCallback, nullptr);
634 252 : opj_set_warning_handler(pCodec, JP2OpenJPEG_WarningCallback, nullptr);
635 252 : opj_set_error_handler(pCodec, JP2OpenJPEG_ErrorCallback, nullptr);
636 :
637 252 : psImage = opj_image_tile_create(
638 : nBands, pasBandParams, static_cast<OPJ_COLOR_SPACE>(eColorSpace));
639 :
640 252 : if (psImage == nullptr)
641 : {
642 0 : CPLError(CE_Failure, CPLE_AppDefined,
643 : "opj_image_tile_create() failed");
644 0 : free();
645 0 : return false;
646 : }
647 :
648 252 : psImage->x0 = 0;
649 252 : psImage->y0 = 0;
650 252 : psImage->x1 = nXSize;
651 252 : psImage->y1 = nYSize;
652 252 : psImage->color_space = static_cast<OPJ_COLOR_SPACE>(eColorSpace);
653 252 : psImage->numcomps = nBands;
654 :
655 252 : if (!opj_setup_encoder(pCodec, &compressParams, psImage))
656 : {
657 0 : CPLError(CE_Failure, CPLE_AppDefined, "opj_setup_encoder() failed");
658 0 : free();
659 0 : return false;
660 : }
661 :
662 : #if IS_OPENJPEG_OR_LATER(2, 4, 0)
663 : if (getenv("OPJ_NUM_THREADS") == nullptr)
664 : opj_codec_set_threads(pCodec, numThreads);
665 : CPLStringList aosOptions;
666 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "PLT", "FALSE")))
667 : {
668 : aosOptions.AddString("PLT=YES");
669 : }
670 :
671 : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
672 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "TLM", "FALSE")))
673 : {
674 : aosOptions.AddString("TLM=YES");
675 : }
676 : #endif
677 :
678 : if (!opj_encoder_set_extra_options(pCodec, aosOptions.List()))
679 : {
680 : CPLError(CE_Failure, CPLE_AppDefined,
681 : "opj_encoder_set_extra_options() failed");
682 : free();
683 : return false;
684 : }
685 : #endif
686 252 : pStream = opj_stream_create(1024 * 1024, FALSE);
687 252 : opj_stream_set_write_function(pStream, JP2Dataset_Write);
688 252 : opj_stream_set_seek_function(pStream, JP2Dataset_Seek);
689 252 : opj_stream_set_skip_function(pStream, JP2Dataset_Skip);
690 252 : opj_stream_set_user_data(pStream, psJP2File, nullptr);
691 :
692 252 : return CPL_TO_BOOL(opj_start_compress(pCodec, psImage, pStream));
693 : }
694 :
695 : /* No-ops: OpenJPEG JP2 boxes are handled by GDAL's GDALJP2Box I/O */
696 : static bool ownsFile()
697 : {
698 : return false;
699 : }
700 :
701 252 : static bool initCodec([[maybe_unused]] const char *pszFilename,
702 : [[maybe_unused]] VSIVirtualHandleUniquePtr &fpOwner)
703 : {
704 252 : return true;
705 : }
706 :
707 0 : static void setupJP2Metadata(bool, int, bool, GDALJP2Metadata *,
708 : GDALJP2Box *, int, int, int, int,
709 : JP2_COLOR_SPACE, int, GDALColorTable *,
710 : GDALDataset *, CSLConstList)
711 : {
712 0 : }
713 :
714 0 : static void extractJP2BoxInfo(int, int &, int &, int &, int &,
715 : GDALColorTable **)
716 : {
717 0 : }
718 :
719 0 : static bool rewriteBoxes(const char *, GDALDataset *)
720 : {
721 0 : return false;
722 : }
723 :
724 0 : static bool transcode(const char *, const char *, GDALDataset *,
725 : CSLConstList)
726 : {
727 0 : return false;
728 : }
729 :
730 407 : bool compressTile(int tileIndex, GByte *buff, uint32_t buffLen)
731 : {
732 407 : if (!pCodec || !pStream)
733 0 : return false;
734 407 : return CPL_TO_BOOL(
735 407 : opj_write_tile(pCodec, tileIndex, buff, buffLen, pStream));
736 : }
737 :
738 251 : bool finishCompress(void)
739 : {
740 251 : bool rc = false;
741 251 : if (pCodec && pStream)
742 251 : rc = CPL_TO_BOOL(opj_end_compress(pCodec, pStream));
743 251 : if (!rc)
744 10 : CPLError(CE_Failure, CPLE_AppDefined, "opj_end_compress() failed");
745 251 : free();
746 251 : return rc;
747 : }
748 :
749 391 : void cleanUpDecompress(void)
750 : {
751 391 : if (pCodec && pStream)
752 391 : opj_end_decompress(pCodec, pStream);
753 391 : free();
754 391 : }
755 :
756 : /************************************************************************/
757 : /* CreateReadStream() */
758 : /************************************************************************/
759 1157 : static jp2_stream *CreateReadStream(JP2File *psJP2File, vsi_l_offset nSize)
760 : {
761 1157 : if (!psJP2File)
762 0 : return nullptr;
763 1157 : auto pStream = opj_stream_create(
764 : 1024, TRUE); // Default 1MB is way too big for some datasets
765 1157 : if (pStream == nullptr)
766 0 : return nullptr;
767 :
768 1157 : VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset, SEEK_SET);
769 1157 : opj_stream_set_user_data_length(pStream, nSize);
770 :
771 1157 : opj_stream_set_read_function(pStream, JP2Dataset_Read);
772 1157 : opj_stream_set_seek_function(pStream, JP2Dataset_Seek);
773 1157 : opj_stream_set_skip_function(pStream, JP2Dataset_Skip);
774 1157 : opj_stream_set_user_data(pStream, psJP2File, nullptr);
775 :
776 1157 : return pStream;
777 : }
778 :
779 : jp2_codec *pCodec = nullptr;
780 : jp2_stream *pStream = nullptr;
781 : jp2_image *psImage = nullptr;
782 : jp2_image_comp_param *pasBandParams = nullptr;
783 : JP2File *psJP2File = nullptr;
784 :
785 : OPJCodecWrapper(const OPJCodecWrapper &) = delete;
786 : OPJCodecWrapper &operator=(const OPJCodecWrapper &) = delete;
787 : };
788 :
789 : /************************************************************************/
790 : /* ==================================================================== */
791 : /* JP2OPJDatasetBase */
792 : /* ==================================================================== */
793 : /************************************************************************/
794 :
795 1905 : struct JP2OPJDatasetBase : public JP2DatasetBase
796 : {
797 : typedef opj_codec_t jp2_codec;
798 : typedef opj_image_t jp2_image;
799 : typedef opj_stream_t jp2_stream;
800 :
801 : typedef opj_image_cmptparm_t jp2_image_comp_param;
802 : typedef opj_image_comp_t jp2_image_comp;
803 :
804 : int eColorSpace = OPJCodecWrapper::cvtenum(JP2_CLRSPC_UNKNOWN);
805 : OPJCodecWrapper *m_codec = nullptr;
806 : int *m_pnLastLevel = nullptr;
807 : bool m_bStrict = true;
808 :
809 : virtual ~JP2OPJDatasetBase();
810 :
811 1905 : void init(void)
812 : {
813 : (void)this;
814 1905 : }
815 :
816 : virtual CPLErr
817 18 : AdviseRead([[maybe_unused]] int nXOff, [[maybe_unused]] int nYOff,
818 : [[maybe_unused]] int nXSize, [[maybe_unused]] int nYSize,
819 : [[maybe_unused]] int nBufXSize, [[maybe_unused]] int nBufYSize,
820 : [[maybe_unused]] GDALDataType eDT,
821 : [[maybe_unused]] int nBandCount,
822 : [[maybe_unused]] int *panBandList,
823 : [[maybe_unused]] CSLConstList papszOptions)
824 : {
825 18 : return CE_None;
826 : }
827 :
828 0 : virtual CPLErr DirectRasterIO(
829 : [[maybe_unused]] GDALRWFlag /* eRWFlag */, [[maybe_unused]] int nXOff,
830 : [[maybe_unused]] int nYOff, [[maybe_unused]] int nXSize,
831 : [[maybe_unused]] int nYSize, [[maybe_unused]] void *pData,
832 : [[maybe_unused]] int nBufXSize, [[maybe_unused]] int nBufYSize,
833 : [[maybe_unused]] GDALDataType eBufType, [[maybe_unused]] int nBandCount,
834 : [[maybe_unused]] const int *panBandMap,
835 : [[maybe_unused]] GSpacing nPixelSpace,
836 : [[maybe_unused]] GSpacing nLineSpace,
837 : [[maybe_unused]] GSpacing nBandSpace,
838 : [[maybe_unused]] GDALRasterIOExtraArg *psExtraArg)
839 :
840 : {
841 0 : return CE_None;
842 : }
843 :
844 2880 : static bool canPerformDirectIO(void)
845 : {
846 2880 : return false;
847 : }
848 :
849 391 : CPLErr readBlockInit(VSILFILE *fpIn, OPJCodecWrapper *codec, int nBlockXOff,
850 : int nBlockYOff, int nRasterXSize, int nRasterYSize,
851 : int nBlockXSize, int nBlockYSize, int nTileNumber)
852 : {
853 : const int nWidthToRead =
854 391 : std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
855 : const int nHeightToRead =
856 391 : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
857 :
858 391 : if (!codec)
859 : {
860 0 : CPLError(CE_Failure, CPLE_AppDefined, "null codec");
861 0 : return CE_Failure;
862 : }
863 :
864 391 : if (m_codec && CPLTestBool(CPLGetConfigOption(
865 : "USE_OPENJPEG_SINGLE_TILE_OPTIM", "YES")))
866 : {
867 0 : if ((*m_pnLastLevel == -1 || *m_pnLastLevel == iLevel) &&
868 0 : codec->pCodec != nullptr && *codec->pStream != nullptr &&
869 0 : m_codec->psImage != nullptr)
870 : {
871 0 : codec->transfer(m_codec);
872 : }
873 : else
874 : {
875 : // For some reason, we need to "reboot" all the machinery if
876 : // changing of overview level. Should be fixed in openjpeg
877 0 : m_codec->free();
878 : }
879 : }
880 391 : *m_pnLastLevel = iLevel;
881 :
882 391 : if (codec->pCodec == nullptr)
883 : {
884 391 : codec->pCodec = opj_create_decompress(static_cast<OPJ_CODEC_FORMAT>(
885 391 : OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
886 391 : if (codec->pCodec == nullptr)
887 : {
888 0 : CPLError(CE_Failure, CPLE_AppDefined,
889 : "opj_create_decompress() failed");
890 0 : return CE_Failure;
891 : }
892 :
893 391 : opj_set_info_handler(codec->pCodec, JP2OpenJPEG_InfoCallback,
894 : nullptr);
895 391 : opj_set_warning_handler(codec->pCodec, JP2OpenJPEG_WarningCallback,
896 : nullptr);
897 391 : opj_set_error_handler(codec->pCodec, JP2OpenJPEG_ErrorCallback,
898 : nullptr);
899 :
900 : opj_dparameters_t parameters;
901 391 : opj_set_default_decoder_parameters(¶meters);
902 391 : if (!opj_setup_decoder(codec->pCodec, ¶meters))
903 : {
904 0 : CPLError(CE_Failure, CPLE_AppDefined,
905 : "opj_setup_decoder() failed");
906 0 : return CE_Failure;
907 : }
908 : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
909 : if (!m_bStrict)
910 : {
911 : opj_decoder_set_strict_mode(codec->pCodec, false);
912 : }
913 : #endif
914 391 : if (m_codec && m_codec->psJP2File)
915 : {
916 0 : codec->pStream = OPJCodecWrapper::CreateReadStream(
917 0 : m_codec->psJP2File, nCodeStreamLength);
918 : }
919 : else
920 : {
921 391 : codec->open(fpIn, nCodeStreamStart);
922 391 : codec->pStream = OPJCodecWrapper::CreateReadStream(
923 : codec->psJP2File, nCodeStreamLength);
924 : }
925 391 : if (!codec->pStream)
926 : {
927 0 : CPLError(CE_Failure, CPLE_AppDefined,
928 : "OPJCodecWrapper::CreateReadStream() failed");
929 0 : return CE_Failure;
930 : }
931 :
932 391 : if (getenv("OPJ_NUM_THREADS") == nullptr)
933 : {
934 391 : if (m_nBlocksToLoad <= 1)
935 254 : opj_codec_set_threads(codec->pCodec, GetNumThreads());
936 : else
937 137 : opj_codec_set_threads(codec->pCodec,
938 137 : GetNumThreads() / m_nBlocksToLoad);
939 : }
940 :
941 391 : if (!opj_read_header(codec->pStream, codec->pCodec,
942 : &codec->psImage))
943 : {
944 0 : CPLError(CE_Failure, CPLE_AppDefined,
945 : "opj_read_header() failed (psImage=%p)",
946 : codec->psImage);
947 : // Hopefully the situation is better on openjpeg 2.2 regarding
948 : // cleanup
949 : // We may leak objects, but the cleanup of openjpeg can cause
950 : // double frees sometimes...
951 0 : return CE_Failure;
952 : }
953 : }
954 391 : if (!opj_set_decoded_resolution_factor(codec->pCodec, iLevel))
955 : {
956 0 : CPLError(CE_Failure, CPLE_AppDefined,
957 : "opj_set_decoded_resolution_factor() failed");
958 0 : return CE_Failure;
959 : }
960 391 : if (bUseSetDecodeArea)
961 : {
962 : /* We need to explicitly set the resolution factor on the image */
963 : /* otherwise opj_set_decode_area() will assume we decode at full */
964 : /* resolution. */
965 : /* If using parameters.cp_reduce instead of
966 : * opj_set_decoded_resolution_factor() */
967 : /* we wouldn't need to do that, as opj_read_header() would automatically
968 : */
969 : /* assign the comps[].factor to the appropriate value */
970 396 : for (unsigned int iBand = 0; iBand < codec->psImage->numcomps;
971 : iBand++)
972 : {
973 199 : codec->psImage->comps[iBand].factor = iLevel;
974 : }
975 : /* The decode area must be expressed in grid reference, ie at full*/
976 : /* scale */
977 197 : if (!opj_set_decode_area(
978 : codec->pCodec, codec->psImage,
979 197 : m_nX0 + static_cast<int>(
980 197 : static_cast<GIntBig>(nBlockXOff * nBlockXSize) *
981 197 : nParentXSize / nRasterXSize),
982 197 : m_nY0 + static_cast<int>(
983 197 : static_cast<GIntBig>(nBlockYOff * nBlockYSize) *
984 197 : nParentYSize / nRasterYSize),
985 197 : m_nX0 + static_cast<int>(
986 197 : static_cast<GIntBig>(nBlockXOff * nBlockXSize +
987 197 : nWidthToRead) *
988 197 : nParentXSize / nRasterXSize),
989 197 : m_nY0 + static_cast<int>(
990 197 : static_cast<GIntBig>(nBlockYOff * nBlockYSize +
991 197 : nHeightToRead) *
992 197 : nParentYSize / nRasterYSize)))
993 : {
994 0 : CPLError(CE_Failure, CPLE_AppDefined,
995 : "opj_set_decode_area() failed");
996 0 : return CE_Failure;
997 : }
998 197 : if (!opj_decode(codec->pCodec, codec->pStream, codec->psImage))
999 : {
1000 0 : CPLError(CE_Failure, CPLE_AppDefined, "opj_decode() failed");
1001 0 : return CE_Failure;
1002 : }
1003 : }
1004 : else
1005 : {
1006 194 : if (!opj_get_decoded_tile(codec->pCodec, codec->pStream,
1007 : codec->psImage, nTileNumber))
1008 : {
1009 0 : CPLError(CE_Failure, CPLE_AppDefined,
1010 : "opj_get_decoded_tile() failed");
1011 0 : return CE_Failure;
1012 : }
1013 : }
1014 :
1015 391 : return CE_None;
1016 : }
1017 :
1018 32 : void cache(CPL_UNUSED JP2OPJDatasetBase *rhs)
1019 : {
1020 : // prevent linter from treating this as potential static method
1021 : (void)this;
1022 32 : if (m_codec && rhs)
1023 0 : m_codec->transfer(rhs->m_codec);
1024 32 : }
1025 :
1026 580 : void cacheNew(CPL_UNUSED OPJCodecWrapper *codec)
1027 : {
1028 : // prevent linter from treating this as potential static method
1029 : (void)this;
1030 580 : if (!codec)
1031 0 : return;
1032 580 : if (m_codec)
1033 0 : m_codec = new OPJCodecWrapper(codec);
1034 : }
1035 :
1036 391 : void cache(CPL_UNUSED OPJCodecWrapper *codec)
1037 : {
1038 : // prevent linter from treating this as potential static method
1039 : (void)this;
1040 391 : if (!codec)
1041 0 : return;
1042 :
1043 391 : if (m_codec && CPLTestBool(CPLGetConfigOption(
1044 : "USE_OPENJPEG_SINGLE_TILE_OPTIM", "YES")))
1045 : {
1046 0 : codec->transfer(m_codec);
1047 : }
1048 : else
1049 : {
1050 391 : codec->cleanUpDecompress();
1051 : }
1052 : }
1053 :
1054 760 : void openCompleteJP2(OPJCodecWrapper *codec)
1055 : {
1056 : // prevent linter from treating this as potential static method
1057 : (void)this;
1058 760 : if (bSingleTiled && bUseSetDecodeArea)
1059 : {
1060 : // nothing
1061 : }
1062 : else
1063 : {
1064 180 : if (codec)
1065 180 : codec->free();
1066 : }
1067 760 : }
1068 :
1069 1905 : void closeJP2(void)
1070 : {
1071 : // prevent linter from treating this as potential static method
1072 : (void)this;
1073 1905 : if (iLevel == 0)
1074 : {
1075 1788 : if (m_codec)
1076 0 : m_codec->free();
1077 1788 : delete m_pnLastLevel;
1078 1788 : m_pnLastLevel = nullptr;
1079 : }
1080 1905 : }
1081 : };
|