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 10750 : static void JP2OpenJPEG_InfoCallback(const char *pszMsg,
84 : CPL_UNUSED void *unused)
85 : {
86 21503 : std::string osMsg(pszMsg);
87 10747 : if (!osMsg.empty() && osMsg.back() == '\n')
88 10751 : osMsg.resize(osMsg.size() - 1);
89 10743 : CPLDebug("JP2OpenJPEG", "info: %s", osMsg.c_str());
90 10752 : }
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 3756 : static size_t JP2Dataset_Read(void *pBuffer, size_t nBytes, void *pUserData)
107 : {
108 3756 : JP2File *psJP2File = static_cast<JP2File *>(pUserData);
109 : size_t nRet =
110 3756 : 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 3755 : if (nRet == 0)
117 2 : nRet = static_cast<size_t>(-1);
118 :
119 3755 : 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 1918 : 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 2181 : {
197 2182 : free();
198 2181 : }
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 3258 : psJP2File = nullptr;
298 3258 : }
299 :
300 759 : static bool preferPerBlockDeCompress(void)
301 : {
302 759 : return true;
303 : }
304 :
305 332960 : static uint32_t stride(jp2_image_comp *comp)
306 : {
307 332960 : 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(char **papszOptions, const std::vector<double> &adfRates,
441 : int nBlockXSize, int nBlockYSize, bool bIsIrreversible,
442 : int nNumResolutions, JP2_PROG_ORDER eProgOrder, int bYCC,
443 : int nCblockW, int nCblockH, int bYCBCR420, int bProfile1,
444 : int nBands, int nXSize, int nYSize,
445 : JP2_COLOR_SPACE eColorSpace,
446 : [[maybe_unused]] int numThreads)
447 : {
448 : int bSOP =
449 251 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SOP", "FALSE"));
450 : int bEPH =
451 251 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "EPH", "FALSE"));
452 :
453 : opj_cparameters_t compressParams;
454 251 : opj_set_default_encoder_parameters(&compressParams);
455 251 : if (bSOP)
456 0 : compressParams.csty |= 0x02;
457 251 : if (bEPH)
458 0 : compressParams.csty |= 0x04;
459 251 : compressParams.cp_disto_alloc = 1;
460 251 : compressParams.tcp_numlayers = static_cast<int>(adfRates.size());
461 578 : for (size_t i = 0; i < adfRates.size(); i++)
462 327 : compressParams.tcp_rates[i] = static_cast<float>(adfRates[i]);
463 251 : compressParams.cp_tx0 = 0;
464 251 : compressParams.cp_ty0 = 0;
465 251 : compressParams.tile_size_on = TRUE;
466 251 : compressParams.cp_tdx = nBlockXSize;
467 251 : compressParams.cp_tdy = nBlockYSize;
468 251 : compressParams.irreversible = bIsIrreversible;
469 251 : compressParams.numresolution = nNumResolutions;
470 251 : compressParams.prog_order = static_cast<OPJ_PROG_ORDER>(eProgOrder);
471 251 : compressParams.tcp_mct = static_cast<char>(bYCC);
472 251 : compressParams.cblockw_init = nCblockW;
473 251 : compressParams.cblockh_init = nCblockH;
474 251 : compressParams.mode = 0;
475 :
476 502 : std::string osComment;
477 251 : const char *pszCOM = CSLFetchNameValue(papszOptions, "COMMENT");
478 251 : if (pszCOM)
479 : {
480 1 : osComment = pszCOM;
481 1 : compressParams.cp_comment = &osComment[0];
482 : }
483 250 : else if (!bIsIrreversible)
484 : {
485 35 : osComment = getComment();
486 35 : if (adfRates.back() == 1.0 && !bYCBCR420)
487 : {
488 33 : osComment += ". LOSSLESS settings used";
489 : }
490 : else
491 : {
492 2 : osComment += ". LOSSY settings used";
493 : }
494 35 : compressParams.cp_comment = &osComment[0];
495 : }
496 :
497 : const char *pszCodeBlockStyle =
498 251 : CSLFetchNameValue(papszOptions, "CODEBLOCK_STYLE");
499 251 : if (pszCodeBlockStyle)
500 : {
501 4 : if (CPLGetValueType(pszCodeBlockStyle) == CPL_VALUE_INTEGER)
502 : {
503 2 : int nVal = atoi(pszCodeBlockStyle);
504 2 : if (nVal >= 0 && nVal <= 63)
505 : {
506 1 : compressParams.mode = nVal;
507 : }
508 : else
509 : {
510 1 : CPLError(CE_Warning, CPLE_NotSupported,
511 : "Invalid value for CODEBLOCK_STYLE: %s. "
512 : "Should be >= 0 and <= 63",
513 : pszCodeBlockStyle);
514 : }
515 : }
516 : else
517 : {
518 : char **papszTokens =
519 2 : CSLTokenizeString2(pszCodeBlockStyle, ", ", 0);
520 9 : for (char **papszIter = papszTokens; papszIter && *papszIter;
521 : ++papszIter)
522 : {
523 7 : if (EQUAL(*papszIter, "BYPASS"))
524 : {
525 1 : compressParams.mode |= (1 << 0);
526 : }
527 6 : else if (EQUAL(*papszIter, "RESET"))
528 : {
529 1 : compressParams.mode |= (1 << 1);
530 : }
531 5 : else if (EQUAL(*papszIter, "TERMALL"))
532 : {
533 1 : compressParams.mode |= (1 << 2);
534 : }
535 4 : else if (EQUAL(*papszIter, "VSC"))
536 : {
537 1 : compressParams.mode |= (1 << 3);
538 : }
539 3 : else if (EQUAL(*papszIter, "PREDICTABLE"))
540 : {
541 1 : compressParams.mode |= (1 << 4);
542 : }
543 2 : else if (EQUAL(*papszIter, "SEGSYM"))
544 : {
545 1 : compressParams.mode |= (1 << 5);
546 : }
547 : else
548 : {
549 1 : CPLError(CE_Warning, CPLE_NotSupported,
550 : "Unrecognized option for CODEBLOCK_STYLE: %s",
551 : *papszIter);
552 : }
553 : }
554 2 : CSLDestroy(papszTokens);
555 : }
556 : }
557 :
558 : /* Add precincts */
559 251 : const char *pszPrecincts = CSLFetchNameValueDef(
560 : papszOptions, "PRECINCTS",
561 : "{512,512},{256,512},{128,512},{64,512},{32,512},{"
562 : "16,512},{8,512},{4,512},{2,512}");
563 : char **papszTokens =
564 251 : CSLTokenizeStringComplex(pszPrecincts, "{},", FALSE, FALSE);
565 251 : int nPrecincts = CSLCount(papszTokens) / 2;
566 2474 : for (int i = 0; i < nPrecincts && i < OPJ_J2K_MAXRLVLS; i++)
567 : {
568 2223 : int nPCRW = atoi(papszTokens[2 * i]);
569 2223 : int nPCRH = atoi(papszTokens[2 * i + 1]);
570 2223 : if (nPCRW < 1 || nPCRH < 1)
571 : break;
572 2223 : compressParams.csty |= 0x01;
573 2223 : compressParams.res_spec++;
574 2223 : compressParams.prcw_init[i] = nPCRW;
575 2223 : compressParams.prch_init[i] = nPCRH;
576 : }
577 251 : CSLDestroy(papszTokens);
578 :
579 : /* Add tileparts setting */
580 : const char *pszTileParts =
581 251 : CSLFetchNameValueDef(papszOptions, "TILEPARTS", "DISABLED");
582 251 : if (EQUAL(pszTileParts, "RESOLUTIONS"))
583 : {
584 1 : compressParams.tp_on = 1;
585 1 : compressParams.tp_flag = 'R';
586 : }
587 250 : else if (EQUAL(pszTileParts, "LAYERS"))
588 : {
589 2 : if (compressParams.tcp_numlayers == 1)
590 : {
591 1 : CPLError(
592 : CE_Warning, CPLE_AppDefined,
593 : "TILEPARTS=LAYERS has no real interest with single-layer "
594 : "codestream");
595 : }
596 2 : compressParams.tp_on = 1;
597 2 : compressParams.tp_flag = 'L';
598 : }
599 248 : else if (EQUAL(pszTileParts, "COMPONENTS"))
600 : {
601 1 : compressParams.tp_on = 1;
602 1 : compressParams.tp_flag = 'C';
603 : }
604 247 : else if (!EQUAL(pszTileParts, "DISABLED"))
605 : {
606 1 : CPLError(CE_Warning, CPLE_NotSupported,
607 : "Invalid value for TILEPARTS");
608 : }
609 :
610 251 : if (bProfile1)
611 : {
612 246 : compressParams.rsiz = OPJ_PROFILE_1;
613 : }
614 :
615 : /* Always ask OpenJPEG to do codestream only. We will take care */
616 : /* of JP2 boxes */
617 251 : pCodec = opj_create_compress(static_cast<OPJ_CODEC_FORMAT>(
618 251 : OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
619 251 : if (pCodec == nullptr)
620 : {
621 0 : CPLError(CE_Failure, CPLE_AppDefined,
622 : "opj_create_compress() failed");
623 0 : return false;
624 : }
625 :
626 251 : opj_set_info_handler(pCodec, JP2OpenJPEG_InfoCallback, nullptr);
627 251 : opj_set_warning_handler(pCodec, JP2OpenJPEG_WarningCallback, nullptr);
628 251 : opj_set_error_handler(pCodec, JP2OpenJPEG_ErrorCallback, nullptr);
629 :
630 251 : psImage = opj_image_tile_create(
631 : nBands, pasBandParams, static_cast<OPJ_COLOR_SPACE>(eColorSpace));
632 :
633 251 : if (psImage == nullptr)
634 : {
635 0 : CPLError(CE_Failure, CPLE_AppDefined,
636 : "opj_image_tile_create() failed");
637 0 : free();
638 0 : return false;
639 : }
640 :
641 251 : psImage->x0 = 0;
642 251 : psImage->y0 = 0;
643 251 : psImage->x1 = nXSize;
644 251 : psImage->y1 = nYSize;
645 251 : psImage->color_space = static_cast<OPJ_COLOR_SPACE>(eColorSpace);
646 251 : psImage->numcomps = nBands;
647 :
648 251 : if (!opj_setup_encoder(pCodec, &compressParams, psImage))
649 : {
650 0 : CPLError(CE_Failure, CPLE_AppDefined, "opj_setup_encoder() failed");
651 0 : free();
652 0 : return false;
653 : }
654 :
655 : #if IS_OPENJPEG_OR_LATER(2, 4, 0)
656 : if (getenv("OPJ_NUM_THREADS") == nullptr)
657 : opj_codec_set_threads(pCodec, numThreads);
658 : CPLStringList aosOptions;
659 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "PLT", "FALSE")))
660 : {
661 : aosOptions.AddString("PLT=YES");
662 : }
663 :
664 : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
665 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "TLM", "FALSE")))
666 : {
667 : aosOptions.AddString("TLM=YES");
668 : }
669 : #endif
670 :
671 : if (!opj_encoder_set_extra_options(pCodec, aosOptions.List()))
672 : {
673 : CPLError(CE_Failure, CPLE_AppDefined,
674 : "opj_encoder_set_extra_options() failed");
675 : free();
676 : return false;
677 : }
678 : #endif
679 251 : pStream = opj_stream_create(1024 * 1024, FALSE);
680 251 : opj_stream_set_write_function(pStream, JP2Dataset_Write);
681 251 : opj_stream_set_seek_function(pStream, JP2Dataset_Seek);
682 251 : opj_stream_set_skip_function(pStream, JP2Dataset_Skip);
683 251 : opj_stream_set_user_data(pStream, psJP2File, nullptr);
684 :
685 251 : return opj_start_compress(pCodec, psImage, pStream);
686 : }
687 :
688 406 : bool compressTile(int tileIndex, GByte *buff, uint32_t buffLen)
689 : {
690 406 : if (!pCodec || !pStream)
691 0 : return false;
692 406 : return opj_write_tile(pCodec, tileIndex, buff, buffLen, pStream);
693 : }
694 :
695 250 : bool finishCompress(void)
696 : {
697 250 : bool rc = false;
698 250 : if (pCodec && pStream)
699 250 : rc = opj_end_compress(pCodec, pStream);
700 250 : if (!rc)
701 10 : CPLError(CE_Failure, CPLE_AppDefined, "opj_end_compress() failed");
702 250 : free();
703 250 : return rc;
704 : }
705 :
706 391 : void cleanUpDecompress(void)
707 : {
708 391 : if (pCodec && pStream)
709 391 : opj_end_decompress(pCodec, pStream);
710 390 : free();
711 391 : }
712 :
713 : /************************************************************************/
714 : /* CreateReadStream() */
715 : /************************************************************************/
716 1156 : static jp2_stream *CreateReadStream(JP2File *psJP2File, vsi_l_offset nSize)
717 : {
718 1156 : if (!psJP2File)
719 0 : return nullptr;
720 1156 : auto pStream = opj_stream_create(
721 : 1024, TRUE); // Default 1MB is way too big for some datasets
722 1156 : if (pStream == nullptr)
723 0 : return nullptr;
724 :
725 1156 : VSIFSeekL(psJP2File->fp_, psJP2File->nBaseOffset, SEEK_SET);
726 1155 : opj_stream_set_user_data_length(pStream, nSize);
727 :
728 1156 : opj_stream_set_read_function(pStream, JP2Dataset_Read);
729 1155 : opj_stream_set_seek_function(pStream, JP2Dataset_Seek);
730 1156 : opj_stream_set_skip_function(pStream, JP2Dataset_Skip);
731 1156 : opj_stream_set_user_data(pStream, psJP2File, nullptr);
732 :
733 1156 : return pStream;
734 : }
735 :
736 : jp2_codec *pCodec = nullptr;
737 : jp2_stream *pStream = nullptr;
738 : jp2_image *psImage = nullptr;
739 : jp2_image_comp_param *pasBandParams = nullptr;
740 : JP2File *psJP2File = nullptr;
741 :
742 : OPJCodecWrapper(const OPJCodecWrapper &) = delete;
743 : OPJCodecWrapper &operator=(const OPJCodecWrapper &) = delete;
744 : };
745 :
746 : /************************************************************************/
747 : /* ==================================================================== */
748 : /* JP2OPJDatasetBase */
749 : /* ==================================================================== */
750 : /************************************************************************/
751 :
752 1902 : struct JP2OPJDatasetBase : public JP2DatasetBase
753 : {
754 : int eColorSpace = OPJCodecWrapper::cvtenum(JP2_CLRSPC_UNKNOWN);
755 : OPJCodecWrapper *m_codec = nullptr;
756 : int *m_pnLastLevel = nullptr;
757 : bool m_bStrict = true;
758 :
759 : virtual ~JP2OPJDatasetBase();
760 :
761 1902 : void init(void)
762 : {
763 : (void)this;
764 1902 : }
765 :
766 1902 : void deinit(void)
767 : {
768 : (void)this;
769 1902 : }
770 :
771 391 : CPLErr readBlockInit(VSILFILE *fpIn, OPJCodecWrapper *codec, int nBlockXOff,
772 : int nBlockYOff, int nRasterXSize, int nRasterYSize,
773 : int nBlockXSize, int nBlockYSize, int nTileNumber)
774 : {
775 : const int nWidthToRead =
776 391 : std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
777 : const int nHeightToRead =
778 391 : std::min(nBlockYSize, nRasterYSize - nBlockYOff * nBlockYSize);
779 :
780 391 : if (!codec)
781 : {
782 0 : CPLError(CE_Failure, CPLE_AppDefined, "null codec");
783 0 : return CE_Failure;
784 : }
785 :
786 391 : if (m_codec && CPLTestBool(CPLGetConfigOption(
787 : "USE_OPENJPEG_SINGLE_TILE_OPTIM", "YES")))
788 : {
789 0 : if ((*m_pnLastLevel == -1 || *m_pnLastLevel == iLevel) &&
790 0 : codec->pCodec != nullptr && *codec->pStream != nullptr &&
791 0 : m_codec->psImage != nullptr)
792 : {
793 0 : codec->transfer(m_codec);
794 : }
795 : else
796 : {
797 : // For some reason, we need to "reboot" all the machinery if
798 : // changing of overview level. Should be fixed in openjpeg
799 0 : m_codec->free();
800 : }
801 : }
802 391 : *m_pnLastLevel = iLevel;
803 :
804 391 : if (codec->pCodec == nullptr)
805 : {
806 391 : codec->pCodec = opj_create_decompress(static_cast<OPJ_CODEC_FORMAT>(
807 391 : OPJCodecWrapper::cvtenum(JP2_CODEC_J2K)));
808 391 : if (codec->pCodec == nullptr)
809 : {
810 0 : CPLError(CE_Failure, CPLE_AppDefined,
811 : "opj_create_decompress() failed");
812 0 : return CE_Failure;
813 : }
814 :
815 391 : opj_set_info_handler(codec->pCodec, JP2OpenJPEG_InfoCallback,
816 : nullptr);
817 391 : opj_set_warning_handler(codec->pCodec, JP2OpenJPEG_WarningCallback,
818 : nullptr);
819 391 : opj_set_error_handler(codec->pCodec, JP2OpenJPEG_ErrorCallback,
820 : nullptr);
821 :
822 : opj_dparameters_t parameters;
823 391 : opj_set_default_decoder_parameters(¶meters);
824 391 : if (!opj_setup_decoder(codec->pCodec, ¶meters))
825 : {
826 0 : CPLError(CE_Failure, CPLE_AppDefined,
827 : "opj_setup_decoder() failed");
828 0 : return CE_Failure;
829 : }
830 : #if IS_OPENJPEG_OR_LATER(2, 5, 0)
831 : if (!m_bStrict)
832 : {
833 : opj_decoder_set_strict_mode(codec->pCodec, false);
834 : }
835 : #endif
836 391 : if (m_codec && m_codec->psJP2File)
837 : {
838 0 : codec->pStream = OPJCodecWrapper::CreateReadStream(
839 0 : m_codec->psJP2File, nCodeStreamLength);
840 : }
841 : else
842 : {
843 391 : codec->open(fpIn, nCodeStreamStart);
844 391 : codec->pStream = OPJCodecWrapper::CreateReadStream(
845 : codec->psJP2File, nCodeStreamLength);
846 : }
847 390 : if (!codec->pStream)
848 : {
849 0 : CPLError(CE_Failure, CPLE_AppDefined,
850 : "OPJCodecWrapper::CreateReadStream() failed");
851 0 : return CE_Failure;
852 : }
853 :
854 390 : if (getenv("OPJ_NUM_THREADS") == nullptr)
855 : {
856 391 : if (m_nBlocksToLoad <= 1)
857 254 : opj_codec_set_threads(codec->pCodec, GetNumThreads());
858 : else
859 137 : opj_codec_set_threads(codec->pCodec,
860 137 : GetNumThreads() / m_nBlocksToLoad);
861 : }
862 :
863 391 : if (!opj_read_header(codec->pStream, codec->pCodec,
864 : &codec->psImage))
865 : {
866 0 : CPLError(CE_Failure, CPLE_AppDefined,
867 : "opj_read_header() failed (psImage=%p)",
868 : codec->psImage);
869 : // Hopefully the situation is better on openjpeg 2.2 regarding
870 : // cleanup
871 : // We may leak objects, but the cleanup of openjpeg can cause
872 : // double frees sometimes...
873 0 : return CE_Failure;
874 : }
875 : }
876 391 : if (!opj_set_decoded_resolution_factor(codec->pCodec, iLevel))
877 : {
878 0 : CPLError(CE_Failure, CPLE_AppDefined,
879 : "opj_set_decoded_resolution_factor() failed");
880 0 : return CE_Failure;
881 : }
882 391 : if (bUseSetDecodeArea)
883 : {
884 : /* We need to explicitly set the resolution factor on the image */
885 : /* otherwise opj_set_decode_area() will assume we decode at full */
886 : /* resolution. */
887 : /* If using parameters.cp_reduce instead of
888 : * opj_set_decoded_resolution_factor() */
889 : /* we wouldn't need to do that, as opj_read_header() would automatically
890 : */
891 : /* assign the comps[].factor to the appropriate value */
892 396 : for (unsigned int iBand = 0; iBand < codec->psImage->numcomps;
893 : iBand++)
894 : {
895 199 : codec->psImage->comps[iBand].factor = iLevel;
896 : }
897 : /* The decode area must be expressed in grid reference, ie at full*/
898 : /* scale */
899 197 : if (!opj_set_decode_area(
900 : codec->pCodec, codec->psImage,
901 197 : m_nX0 + static_cast<int>(
902 197 : static_cast<GIntBig>(nBlockXOff * nBlockXSize) *
903 197 : nParentXSize / nRasterXSize),
904 197 : m_nY0 + static_cast<int>(
905 197 : static_cast<GIntBig>(nBlockYOff * nBlockYSize) *
906 197 : nParentYSize / nRasterYSize),
907 197 : m_nX0 + static_cast<int>(
908 197 : static_cast<GIntBig>(nBlockXOff * nBlockXSize +
909 197 : nWidthToRead) *
910 197 : nParentXSize / nRasterXSize),
911 197 : m_nY0 + static_cast<int>(
912 197 : static_cast<GIntBig>(nBlockYOff * nBlockYSize +
913 197 : nHeightToRead) *
914 197 : nParentYSize / nRasterYSize)))
915 : {
916 0 : CPLError(CE_Failure, CPLE_AppDefined,
917 : "opj_set_decode_area() failed");
918 0 : return CE_Failure;
919 : }
920 197 : if (!opj_decode(codec->pCodec, codec->pStream, codec->psImage))
921 : {
922 0 : CPLError(CE_Failure, CPLE_AppDefined, "opj_decode() failed");
923 0 : return CE_Failure;
924 : }
925 : }
926 : else
927 : {
928 194 : if (!opj_get_decoded_tile(codec->pCodec, codec->pStream,
929 : codec->psImage, nTileNumber))
930 : {
931 0 : CPLError(CE_Failure, CPLE_AppDefined,
932 : "opj_get_decoded_tile() failed");
933 0 : return CE_Failure;
934 : }
935 : }
936 :
937 391 : return CE_None;
938 : }
939 :
940 32 : void cache(CPL_UNUSED JP2OPJDatasetBase *rhs)
941 : {
942 : // prevent linter from treating this as potential static method
943 : (void)this;
944 32 : if (m_codec && rhs)
945 0 : m_codec->transfer(rhs->m_codec);
946 32 : }
947 :
948 579 : void cacheNew(CPL_UNUSED OPJCodecWrapper *codec)
949 : {
950 : // prevent linter from treating this as potential static method
951 : (void)this;
952 579 : if (!codec)
953 0 : return;
954 579 : if (m_codec)
955 0 : m_codec = new OPJCodecWrapper(codec);
956 : }
957 :
958 391 : void cache(CPL_UNUSED OPJCodecWrapper *codec)
959 : {
960 : // prevent linter from treating this as potential static method
961 : (void)this;
962 391 : if (!codec)
963 0 : return;
964 :
965 391 : if (m_codec && CPLTestBool(CPLGetConfigOption(
966 : "USE_OPENJPEG_SINGLE_TILE_OPTIM", "YES")))
967 : {
968 0 : codec->transfer(m_codec);
969 : }
970 : else
971 : {
972 391 : codec->cleanUpDecompress();
973 : }
974 : }
975 :
976 759 : void openCompleteJP2(OPJCodecWrapper *codec)
977 : {
978 : // prevent linter from treating this as potential static method
979 : (void)this;
980 759 : if (bSingleTiled && bUseSetDecodeArea)
981 : {
982 : // nothing
983 : }
984 : else
985 : {
986 180 : if (codec)
987 180 : codec->free();
988 : }
989 759 : }
990 :
991 1902 : void closeJP2(void)
992 : {
993 : // prevent linter from treating this as potential static method
994 : (void)this;
995 1902 : if (iLevel == 0)
996 : {
997 1785 : if (m_codec)
998 0 : m_codec->free();
999 1785 : delete m_pnLastLevel;
1000 1785 : m_pnLastLevel = nullptr;
1001 : }
1002 1902 : }
1003 : };
|