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