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