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