Coverage Report

Created: 2025-06-09 06:28

/src/grok/src/lib/core/grok.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 *    Copyright (C) 2016-2025 Grok Image Compression Inc.
3
 *
4
 *    This source code is free software: you can redistribute it and/or  modify
5
 *    it under the terms of the GNU Affero General Public License, version 3,
6
 *    as published by the Free Software Foundation.
7
 *
8
 *    This source code is distributed in the hope that it will be useful,
9
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 *    GNU Affero General Public License for more details.
12
 *
13
 *    You should have received a copy of the GNU Affero General Public License
14
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 *
16
 *
17
 *    This source code incorporates work covered by the BSD 2-clause license.
18
 *    Please see the LICENSE file in the root directory for details.
19
 *
20
 */
21
22
#ifdef _WIN32
23
#include <windows.h>
24
#else /* _WIN32 */
25
#include <sys/stat.h>
26
#include <unistd.h>
27
#include <sys/mman.h>
28
#endif
29
#include <fcntl.h>
30
31
#include "grk_includes.h"
32
using namespace grk;
33
34
struct GrkCodec
35
{
36
  explicit GrkCodec(grk_stream* stream);
37
  ~GrkCodec();
38
39
  static GrkCodec* getImpl(grk_object* codec)
40
3.38k
  {
41
3.38k
    return ((GrkObjectWrapperImpl<GrkCodec>*)codec->wrapper)->getWrappee();
42
3.38k
  }
43
  grk_object* getWrapper(void)
44
0
  {
45
0
    return &obj;
46
0
  }
47
48
  grk_object obj;
49
  ICodeStreamCompress* compressor_;
50
  ICodeStreamDecompress* decompressor_;
51
52
private:
53
  grk_stream* stream_;
54
};
55
56
GrkCodec::GrkCodec(grk_stream* stream)
57
752
    : compressor_(nullptr), decompressor_(nullptr), stream_(stream)
58
752
{
59
752
  obj.wrapper = new GrkObjectWrapperImpl<GrkCodec>(this);
60
752
}
61
62
GrkCodec::~GrkCodec()
63
752
{
64
752
  delete compressor_;
65
752
  delete decompressor_;
66
752
  grk_object_unref(stream_);
67
752
}
68
69
/**
70
 * Start compressing image
71
 *
72
 * @param codec         compression codec
73
 *
74
 */
75
static bool grk_compress_start(grk_object* codec);
76
77
/** Create stream from a file identified with its filename with a specific buffer size
78
 *
79
 * @param fname           the name of the file to stream
80
 * @param buffer_size     size of the chunk used to stream
81
 * @param is_read_stream  whether the stream is a read stream (true) or not (false)
82
 */
83
static grk_stream* grk_stream_create_file_stream(const char* fname, size_t buffer_size,
84
                                                 bool is_read_stream);
85
86
static grk_stream* grk_stream_create_stream(grk_stream_params* stream_params);
87
88
static grk_stream* grk_stream_new(size_t buffer_size, bool is_input)
89
0
{
90
0
  auto streamImpl = new BufferedStream(nullptr, buffer_size, is_input);
91
92
0
  return streamImpl->getWrapper();
93
0
}
94
95
grk_object* grk_decompress_create(grk_stream* stream)
96
752
{
97
752
  GrkCodec* codec = nullptr;
98
752
  auto bstream = BufferedStream::getImpl(stream);
99
752
  auto format = bstream->getFormat();
100
752
  if(format == GRK_CODEC_UNK)
101
0
  {
102
0
    grklog.error("Invalid codec format.");
103
0
    return nullptr;
104
0
  }
105
752
  codec = new GrkCodec(stream);
106
752
  if(format == GRK_CODEC_J2K)
107
588
    codec->decompressor_ = new CodeStreamDecompress(BufferedStream::getImpl(stream));
108
164
  else
109
164
    codec->decompressor_ = new FileFormatDecompress(BufferedStream::getImpl(stream));
110
111
752
  return &codec->obj;
112
752
}
113
114
static void infoCallback(const char* msg, [[maybe_unused]] void* client_data)
115
0
{
116
0
  auto t = std::string(msg) + "\n";
117
0
  fprintf(stdout, "[INFO] %s", t.c_str());
118
0
}
119
static void debugCallback(const char* msg, [[maybe_unused]] void* client_data)
120
0
{
121
0
  auto t = std::string(msg) + "\n";
122
0
  fprintf(stdout, "[DEBUG] %s", t.c_str());
123
0
}
124
static void traceCallback(const char* msg, [[maybe_unused]] void* client_data)
125
0
{
126
0
  auto t = std::string(msg) + "\n";
127
0
  fprintf(stdout, "[TRACE] %s", t.c_str());
128
0
}
129
static void warningCallback(const char* msg, [[maybe_unused]] void* client_data)
130
0
{
131
0
  auto t = std::string(msg) + "\n";
132
0
  fprintf(stdout, "[WARNING] %s", t.c_str());
133
0
}
134
135
static void errorCallback(const char* msg, [[maybe_unused]] void* client_data)
136
0
{
137
0
  auto t = std::string(msg) + "\n";
138
0
  fprintf(stderr, "%s", t.c_str());
139
0
}
140
141
struct InitState
142
{
143
  InitState(const char* pluginPath, uint32_t numThreads)
144
4
      : pluginPath_(pluginPath), numThreads_(numThreads), initialized_(false),
145
4
        pluginInitialized_(false)
146
4
  {}
147
2
  InitState(void) : InitState(nullptr, 0) {}
148
  bool operator==(const InitState& rhs) const
149
0
  {
150
0
    return pluginPath_ == rhs.pluginPath_ && numThreads_ == rhs.numThreads_;
151
0
  }
152
  const char* pluginPath_;
153
  uint32_t numThreads_;
154
  bool initialized_;
155
  bool pluginInitialized_;
156
};
157
158
static InitState initState_;
159
bool grk_initialize(const char* pluginPath, uint32_t numThreads)
160
2
{
161
2
  const char* singleThreadEnv = std::getenv("GRK_TEST_SINGLE");
162
2
  if(singleThreadEnv && std::atoi(singleThreadEnv) == 1)
163
0
  {
164
0
    numThreads = 1; // Force single-threaded execution
165
0
  }
166
167
2
  InitState newState(pluginPath, numThreads);
168
2
  if(initState_.initialized_ && newState == initState_)
169
0
    return true;
170
  // 1. set up executor
171
2
  ExecSingleton::create(numThreads);
172
173
2
  if(!grklog.info_handler)
174
2
  {
175
2
    grk_msg_handlers handlers = {};
176
2
    const char* debug_env = std::getenv("GRK_DEBUG");
177
2
    if(debug_env)
178
0
    {
179
0
      int level = std::atoi(debug_env);
180
0
      if(level >= 1)
181
0
        handlers.error_callback = errorCallback;
182
0
      if(level >= 2)
183
0
        handlers.warn_callback = warningCallback;
184
0
      if(level >= 3)
185
0
        handlers.info_callback = infoCallback;
186
0
      if(level >= 4)
187
0
        handlers.debug_callback = debugCallback;
188
0
      if(level >= 5)
189
0
        handlers.trace_callback = traceCallback;
190
0
    }
191
2
    grk_set_msg_handlers(handlers);
192
2
  }
193
194
2
  initState_ = newState;
195
196
  // 2. try to load plugin
197
2
  if(!initState_.pluginInitialized_)
198
2
  {
199
2
    grk_plugin_load_info info;
200
2
    info.pluginPath = pluginPath;
201
2
    initState_.pluginInitialized_ = grk_plugin_load(info);
202
2
    if(!initState_.pluginInitialized_)
203
2
      return false;
204
0
    else
205
0
      grklog.info("Plugin loaded");
206
2
  }
207
208
0
  return true;
209
2
}
210
211
GRK_API void GRK_CALLCONV grk_deinitialize()
212
0
{
213
0
  grk_plugin_cleanup();
214
0
  ExecSingleton::destroy();
215
0
}
216
217
GRK_API grk_object* GRK_CALLCONV grk_object_ref(grk_object* obj)
218
11.1k
{
219
11.1k
  if(!obj)
220
0
    return nullptr;
221
11.1k
  auto wrapper = (GrkObjectWrapper*)obj->wrapper;
222
11.1k
  wrapper->ref();
223
224
11.1k
  return obj;
225
11.1k
}
226
GRK_API void GRK_CALLCONV grk_object_unref(grk_object* obj)
227
25.4k
{
228
25.4k
  if(!obj)
229
3
    return;
230
25.3k
  GrkObjectWrapper* wrapper = (GrkObjectWrapper*)obj->wrapper;
231
25.3k
  if(wrapper->unref() == 0)
232
14.2k
    delete wrapper;
233
25.3k
}
234
235
GRK_API void GRK_CALLCONV grk_set_msg_handlers(grk_msg_handlers msg_handlers)
236
2
{
237
2
  grklog.info_handler = msg_handlers.info_callback;
238
2
  grklog.info_data_ = msg_handlers.info_data;
239
2
  grklog.debug_handler = msg_handlers.debug_callback;
240
2
  grklog.debug_data_ = msg_handlers.debug_data;
241
2
  grklog.trace_handler = msg_handlers.trace_callback;
242
2
  grklog.trace_data_ = msg_handlers.trace_data;
243
2
  grklog.warning_handler = msg_handlers.warn_callback;
244
2
  grklog.warning_data_ = msg_handlers.warn_data;
245
2
  grklog.error_handler = msg_handlers.error_callback;
246
2
  grklog.error_data_ = msg_handlers.error_data;
247
2
}
248
249
static size_t grk_read_from_file(uint8_t* buffer, size_t numBytes, void* p_file)
250
0
{
251
0
  return fread(buffer, 1, numBytes, (FILE*)p_file);
252
0
}
253
254
static uint64_t grk_get_data_length_from_file(void* filePtr)
255
0
{
256
0
  auto file = (FILE*)filePtr;
257
0
  GRK_FSEEK(file, 0, SEEK_END);
258
0
  int64_t file_length = (int64_t)GRK_FTELL(file);
259
0
  GRK_FSEEK(file, 0, SEEK_SET);
260
0
  return (uint64_t)file_length;
261
0
}
262
static size_t grk_write_to_file(const uint8_t* buffer, size_t numBytes, void* p_file)
263
0
{
264
0
  return fwrite(buffer, 1, numBytes, (FILE*)p_file);
265
0
}
266
static bool grk_seek_in_file(uint64_t numBytes, void* p_user_data)
267
0
{
268
0
  if(numBytes > INT64_MAX)
269
0
  {
270
0
    return false;
271
0
  }
272
273
0
  return GRK_FSEEK((FILE*)p_user_data, (int64_t)numBytes, SEEK_SET) ? false : true;
274
0
}
275
276
#ifdef _WIN32
277
#ifndef GRK_STATIC
278
BOOL APIENTRY DllMain([[maybe_unused]] HINSTANCE hModule, DWORD ul_reason_for_call,
279
                      [[maybe_unused]] LPVOID lpReserved)
280
{
281
  switch(ul_reason_for_call)
282
  {
283
    case DLL_PROCESS_ATTACH:
284
      break;
285
    case DLL_PROCESS_DETACH:
286
      break;
287
    case DLL_THREAD_ATTACH:
288
    case DLL_THREAD_DETACH:
289
      break;
290
  }
291
  return TRUE;
292
}
293
#endif /* GRK_STATIC */
294
#endif /* _WIN32 */
295
296
const char* GRK_CALLCONV grk_version(void)
297
0
{
298
0
  return GRK_PACKAGE_VERSION;
299
0
}
300
301
grk_image* GRK_CALLCONV grk_image_new(uint16_t numcmpts, grk_image_comp* cmptparms,
302
                                      GRK_COLOR_SPACE clrspc, bool alloc_data)
303
0
{
304
0
  return GrkImage::create(nullptr, numcmpts, cmptparms, clrspc, alloc_data);
305
0
}
306
307
grk_image_meta* GRK_CALLCONV grk_image_meta_new(void)
308
752
{
309
752
  return (grk_image_meta*)(new GrkImageMeta());
310
752
}
311
312
/* DECOMPRESSION FUNCTIONS*/
313
314
static const char* JP2_RFC3745_MAGIC = "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a";
315
static const char* J2K_CODESTREAM_MAGIC = "\xff\x4f\xff\x51";
316
bool grk_decompress_buffer_detect_format(uint8_t* buffer, size_t len, GRK_CODEC_FORMAT* fmt)
317
755
{
318
755
  GRK_CODEC_FORMAT magic_format = GRK_CODEC_UNK;
319
755
  if(len < 12)
320
0
    return false;
321
322
755
  if(memcmp(buffer, JP2_RFC3745_MAGIC, 12) == 0)
323
164
  {
324
164
    magic_format = GRK_CODEC_JP2;
325
164
    grklog.debug("Detected JP2 image format");
326
164
  }
327
591
  else if(memcmp(buffer, J2K_CODESTREAM_MAGIC, 4) == 0)
328
588
  {
329
588
    magic_format = GRK_CODEC_J2K;
330
588
    grklog.debug("Detected J2K image format");
331
588
  }
332
3
  else
333
3
  {
334
3
    grklog.error("No JPEG 2000 code stream detected.");
335
3
    *fmt = GRK_CODEC_UNK;
336
337
3
    return false;
338
3
  }
339
752
  *fmt = magic_format;
340
341
752
  return true;
342
755
}
343
bool GRK_CALLCONV grk_decompress_detect_format(const char* fileName, GRK_CODEC_FORMAT* fmt)
344
0
{
345
0
  uint8_t buf[12];
346
0
  size_t bytesRead;
347
348
0
  auto reader = fopen(fileName, "rb");
349
0
  if(!reader)
350
0
  {
351
0
    grklog.error("Unable to open file %s.", fileName);
352
0
    return false;
353
0
  }
354
355
0
  bytesRead = fread(buf, 1, 12, reader);
356
0
  if(fclose(reader))
357
0
  {
358
0
    grklog.error("Unable to close file {}.", fileName);
359
0
    return false;
360
0
  }
361
0
  if(bytesRead != 12)
362
0
  {
363
0
    grklog.error("Insufficient bytes to detect JPEG 2000 format in file {}.", fileName);
364
0
    return false;
365
0
  }
366
367
0
  return grk_decompress_buffer_detect_format(buf, 12, fmt);
368
0
}
369
370
static grk_object* grk_decompress_create_from_buffer(uint8_t* buf, size_t len)
371
755
{
372
755
  auto stream = create_mem_stream(buf, len, false, true);
373
755
  if(!stream)
374
3
  {
375
3
    grklog.error("Unable to create memory stream.");
376
3
    return nullptr;
377
3
  }
378
752
  auto codec = grk_decompress_create(stream);
379
752
  if(!codec)
380
0
  {
381
0
    grklog.error("Unable to create codec");
382
0
    grk_object_unref(stream);
383
0
    return nullptr;
384
0
  }
385
386
752
  return codec;
387
752
}
388
389
static grk_object* grk_decompress_create_from_callbacks(grk_stream_params* stream_params)
390
0
{
391
0
  auto stream = grk_stream_create_stream(stream_params);
392
0
  if(!stream)
393
0
  {
394
0
    grklog.error("Unable to create callback stream.");
395
0
    return nullptr;
396
0
  }
397
0
  auto codec = grk_decompress_create(stream);
398
0
  if(!codec)
399
0
  {
400
0
    grklog.error("Unable to create codec");
401
0
    grk_object_unref(stream);
402
0
    return nullptr;
403
0
  }
404
405
0
  return codec;
406
0
}
407
408
static grk_object* grk_decompress_create_from_file(const char* file_name)
409
0
{
410
0
  auto stream = grk_stream_create_file_stream(file_name, 1000000, true);
411
0
  if(!stream)
412
0
  {
413
0
    grklog.error("Unable to create stream for file %s.", file_name);
414
0
    return nullptr;
415
0
  }
416
0
  auto codec = grk_decompress_create(stream);
417
0
  if(!codec)
418
0
  {
419
0
    grklog.error("Unable to create codec for file %s", file_name);
420
0
    grk_object_unref(stream);
421
0
    return nullptr;
422
0
  }
423
424
0
  return codec;
425
0
}
426
427
grk_object* GRK_CALLCONV grk_decompress_init(grk_stream_params* stream_params,
428
                                             grk_decompress_parameters* params)
429
755
{
430
755
  if(!params)
431
0
  {
432
0
    std::cerr << "grk_decompress_init: decompress parameters cannot be null.\n";
433
0
    return nullptr;
434
0
  }
435
755
  if(!stream_params)
436
0
  {
437
0
    std::cerr << "grk_decompress_init: stream parameters cannot be null"
438
0
                 " when creating decompression codec.\n";
439
0
    return nullptr;
440
0
  }
441
755
  grk_object* codec = nullptr;
442
755
  if(stream_params->file)
443
0
    codec = grk_decompress_create_from_file(stream_params->file);
444
755
  else if(stream_params->buf)
445
755
    codec = grk_decompress_create_from_buffer(stream_params->buf, stream_params->buf_len);
446
0
  else if(stream_params->read_fn)
447
0
  {
448
0
    codec = grk_decompress_create_from_callbacks(stream_params);
449
0
  }
450
755
  if(!codec)
451
3
    return nullptr;
452
453
752
  auto codecImpl = GrkCodec::getImpl(codec);
454
752
  if(!codecImpl->decompressor_)
455
0
  {
456
0
    grk_object_unref(codec);
457
458
0
    return nullptr;
459
0
  }
460
752
  codecImpl->decompressor_->init(&params->core);
461
462
752
  return codec;
463
752
}
464
bool GRK_CALLCONV grk_decompress_read_header(grk_object* codecWrapper, grk_header_info* header_info)
465
752
{
466
752
  if(codecWrapper)
467
752
  {
468
752
    auto codec = GrkCodec::getImpl(codecWrapper);
469
752
    if(!codec->decompressor_)
470
0
      return false;
471
752
    bool rc = codec->decompressor_->readHeader(header_info);
472
752
    rc &= codec->decompressor_->preProcess();
473
474
752
    return rc;
475
752
  }
476
0
  return false;
477
752
}
478
bool GRK_CALLCONV grk_decompress_set_window(grk_object* codecWrapper, double start_x,
479
                                            double start_y, double end_x, double end_y)
480
626
{
481
626
  if(codecWrapper)
482
626
  {
483
626
    auto codec = GrkCodec::getImpl(codecWrapper);
484
626
    return codec->decompressor_ ? codec->decompressor_->setDecompressRegion(
485
626
                                      grk_rect_double(start_x, start_y, end_x, end_y))
486
626
                                : false;
487
626
  }
488
0
  return false;
489
626
}
490
bool GRK_CALLCONV grk_decompress(grk_object* codecWrapper, grk_plugin_tile* tile)
491
626
{
492
626
  if(codecWrapper)
493
626
  {
494
626
    auto codec = GrkCodec::getImpl(codecWrapper);
495
626
    bool rc = codec->decompressor_ ? codec->decompressor_->decompress(tile) : false;
496
626
    rc = rc && (codec->decompressor_ ? codec->decompressor_->postProcess() : false);
497
498
626
    return rc;
499
626
  }
500
0
  return false;
501
626
}
502
503
void GRK_CALLCONV grk_decompress_wait(grk_object* codecWrapper)
504
0
{
505
0
  (void)codecWrapper;
506
0
}
507
bool GRK_CALLCONV grk_decompress_tile(grk_object* codecWrapper, uint16_t tile_index)
508
0
{
509
0
  if(codecWrapper)
510
0
  {
511
0
    auto codec = GrkCodec::getImpl(codecWrapper);
512
0
    bool rc = codec->decompressor_ ? codec->decompressor_->decompressTile(tile_index) : false;
513
0
    rc = rc && (codec->decompressor_ ? codec->decompressor_->postProcess() : false);
514
0
    return rc;
515
0
  }
516
0
  return false;
517
0
}
518
void GRK_CALLCONV grk_dump_codec(grk_object* codecWrapper, uint32_t info_flag, FILE* output_stream)
519
0
{
520
0
  assert(codecWrapper);
521
0
  if(codecWrapper)
522
0
  {
523
0
    auto codec = GrkCodec::getImpl(codecWrapper);
524
0
    if(codec->decompressor_)
525
0
      codec->decompressor_->dump(info_flag, output_stream);
526
0
  }
527
0
}
528
529
bool grk_set_MCT(grk_cparameters* parameters, const float* pEncodingMatrix, int32_t* p_dc_shift,
530
                 uint32_t pNbComp)
531
0
{
532
0
  uint32_t l_matrix_size = pNbComp * pNbComp * (uint32_t)sizeof(float);
533
0
  uint32_t l_dc_shift_size = pNbComp * (uint32_t)sizeof(int32_t);
534
0
  uint32_t l_mct_total_size = l_matrix_size + l_dc_shift_size;
535
536
  /* add MCT capability */
537
0
  if(GRK_IS_PART2(parameters->rsiz))
538
0
  {
539
0
    parameters->rsiz |= GRK_EXTENSION_MCT;
540
0
  }
541
0
  else
542
0
  {
543
0
    parameters->rsiz = ((GRK_PROFILE_PART2) | (GRK_EXTENSION_MCT));
544
0
  }
545
0
  parameters->irreversible = true;
546
547
  /* use array based MCT */
548
0
  parameters->mct = 2;
549
0
  parameters->mct_data = grk_malloc(l_mct_total_size);
550
0
  if(!parameters->mct_data)
551
0
  {
552
0
    return false;
553
0
  }
554
0
  memcpy(parameters->mct_data, pEncodingMatrix, l_matrix_size);
555
0
  memcpy(((uint8_t*)parameters->mct_data) + l_matrix_size, p_dc_shift, l_dc_shift_size);
556
0
  return true;
557
0
}
558
grk_image* GRK_CALLCONV grk_decompress_get_tile_image(grk_object* codecWrapper, uint16_t tile_index)
559
0
{
560
0
  if(!codecWrapper)
561
0
    return nullptr;
562
0
  auto codec = GrkCodec::getImpl(codecWrapper);
563
0
  if(!codec->decompressor_)
564
0
    return nullptr;
565
0
  auto img = codec->decompressor_->getImage(tile_index);
566
0
  if(!img)
567
0
    img = codec->decompressor_->getImage();
568
0
  return img;
569
0
}
570
571
// no-op
572
grk_progression_state GRK_CALLCONV grk_decompress_get_progression_state(grk_object* codec,
573
                                                                        uint16_t tile_index)
574
0
{
575
0
  (void)codec;
576
0
  (void)tile_index;
577
0
  return {};
578
0
}
579
// no-op
580
bool GRK_CALLCONV grk_decompress_set_progression_state(grk_object* codec,
581
                                                       grk_progression_state state)
582
0
{
583
0
  (void)codec;
584
0
  (void)state;
585
0
  return true;
586
0
}
587
grk_image* GRK_CALLCONV grk_decompress_get_image(grk_object* codecWrapper)
588
626
{
589
626
  if(codecWrapper)
590
626
  {
591
626
    auto codec = GrkCodec::getImpl(codecWrapper);
592
626
    return codec->decompressor_ ? codec->decompressor_->getImage() : nullptr;
593
626
  }
594
0
  return nullptr;
595
626
}
596
597
/* COMPRESSION FUNCTIONS*/
598
599
grk_object* GRK_CALLCONV grk_compress_create(GRK_CODEC_FORMAT p_format, grk_stream* stream)
600
0
{
601
0
  GrkCodec* codec = nullptr;
602
0
  switch(p_format)
603
0
  {
604
0
    case GRK_CODEC_J2K:
605
0
      codec = new GrkCodec(stream);
606
0
      codec->compressor_ = new CodeStreamCompress(BufferedStream::getImpl(stream));
607
0
      break;
608
0
    case GRK_CODEC_JP2:
609
0
      codec = new GrkCodec(stream);
610
0
      codec->compressor_ = new FileFormatCompress(BufferedStream::getImpl(stream));
611
0
      break;
612
0
    default:
613
0
      return nullptr;
614
0
  }
615
0
  return &codec->obj;
616
0
}
617
void GRK_CALLCONV grk_compress_set_default_params(grk_cparameters* parameters)
618
0
{
619
0
  if(!parameters)
620
0
    return;
621
622
0
  memset(parameters, 0, sizeof(grk_cparameters));
623
  /* default coding parameters */
624
0
  parameters->rsiz = GRK_PROFILE_NONE;
625
0
  parameters->max_comp_size = 0;
626
0
  parameters->numresolution = GRK_DEFAULT_NUMRESOLUTION;
627
0
  parameters->cblockw_init = GRK_COMP_PARAM_DEFAULT_CBLOCKW;
628
0
  parameters->cblockh_init = GRK_COMP_PARAM_DEFAULT_CBLOCKH;
629
0
  parameters->numgbits = 2;
630
0
  parameters->prog_order = GRK_DEFAULT_PROG_ORDER;
631
0
  parameters->roi_compno = -1; /* no ROI */
632
0
  parameters->subsampling_dx = 1;
633
0
  parameters->subsampling_dy = 1;
634
0
  parameters->enable_tile_part_generation = false;
635
0
  parameters->decod_format = GRK_FMT_UNK;
636
0
  parameters->cod_format = GRK_FMT_UNK;
637
0
  parameters->layer_rate[0] = 0;
638
0
  parameters->numlayers = 0;
639
0
  parameters->allocation_by_rate_distortion = false;
640
0
  parameters->allocation_by_quality = false;
641
0
  parameters->write_plt = false;
642
0
  parameters->write_tlm = false;
643
0
  parameters->device_id = 0;
644
0
  parameters->repeats = 1;
645
0
}
646
grk_object* GRK_CALLCONV grk_compress_init(grk_stream_params* stream_params,
647
                                           grk_cparameters* parameters, grk_image* image)
648
0
{
649
0
  if(!parameters || !image)
650
0
    return nullptr;
651
0
  if(parameters->cod_format != GRK_FMT_J2K && parameters->cod_format != GRK_FMT_JP2)
652
0
  {
653
0
    grklog.error("Unknown stream format.");
654
0
    return nullptr;
655
0
  }
656
0
  grk_stream* stream = nullptr;
657
0
  if(stream_params->buf)
658
0
  {
659
    // let stream clean up compress buffer
660
0
    stream = create_mem_stream(stream_params->buf, stream_params->buf_len, false, false);
661
0
  }
662
0
  else if(stream_params->file)
663
0
  {
664
0
    stream = grk_stream_create_file_stream(stream_params->file, 1024 * 1024, false);
665
0
  }
666
0
  else if(stream_params->write_fn)
667
0
  {
668
0
    stream = grk_stream_create_stream(stream_params);
669
0
  }
670
0
  if(!stream)
671
0
  {
672
0
    grklog.error("failed to create stream");
673
0
    return nullptr;
674
0
  }
675
676
0
  grk_object* codecWrapper = nullptr;
677
0
  switch(parameters->cod_format)
678
0
  {
679
0
    case GRK_FMT_J2K: /* JPEG 2000 code stream */
680
0
      codecWrapper = grk_compress_create(GRK_CODEC_J2K, stream);
681
0
      break;
682
0
    case GRK_FMT_JP2: /* JPEG 2000 compressed image data */
683
0
      codecWrapper = grk_compress_create(GRK_CODEC_JP2, stream);
684
0
      break;
685
0
    default:
686
0
      break;
687
0
  }
688
689
0
  auto codec = GrkCodec::getImpl(codecWrapper);
690
0
  bool rc = codec->compressor_ ? codec->compressor_->init(parameters, (GrkImage*)image) : false;
691
0
  if(rc)
692
0
  {
693
0
    rc = grk_compress_start(codecWrapper);
694
0
  }
695
0
  else
696
0
  {
697
0
    grklog.error("Failed to initialize codec.");
698
0
    grk_object_unref(codecWrapper);
699
0
    codecWrapper = nullptr;
700
0
  }
701
702
0
  return rc ? codecWrapper : nullptr;
703
0
}
704
705
// no-op
706
bool GRK_CALLCONV grk_decompress_update(grk_decompress_parameters* params, grk_object* codec)
707
0
{
708
0
  (void)params;
709
0
  (void)codec;
710
0
  return false;
711
0
}
712
713
static bool grk_compress_start(grk_object* codecWrapper)
714
0
{
715
0
  if(codecWrapper)
716
0
  {
717
0
    auto codec = GrkCodec::getImpl(codecWrapper);
718
0
    return codec->compressor_ ? codec->compressor_->start() : false;
719
0
  }
720
0
  return false;
721
0
}
722
723
uint64_t GRK_CALLCONV grk_compress(grk_object* codecWrapper, grk_plugin_tile* tile)
724
0
{
725
0
  if(codecWrapper)
726
0
  {
727
0
    auto codec = GrkCodec::getImpl(codecWrapper);
728
0
    return codec->compressor_ ? codec->compressor_->compress(tile) : 0;
729
0
  }
730
0
  return 0;
731
0
}
732
static void grkFree_file(void* p_user_data)
733
0
{
734
0
  if(p_user_data)
735
0
    fclose((FILE*)p_user_data);
736
0
}
737
738
static grk_stream* grk_stream_create_stream(grk_stream_params* stream_params)
739
0
{
740
0
  bool readStream = stream_params->read_fn;
741
0
  size_t doubleBufferLen = 16 * 1024 * 1024;
742
0
  if(stream_params->stream_len)
743
0
    doubleBufferLen = std::min(doubleBufferLen, stream_params->stream_len);
744
0
  auto stream = grk_stream_new(doubleBufferLen, readStream);
745
0
  if(!stream)
746
0
    return nullptr;
747
  // validate
748
0
  if(readStream)
749
0
  {
750
0
    auto bstream = BufferedStream::getImpl(stream);
751
0
    uint8_t buf[12];
752
0
    size_t bytesRead = stream_params->read_fn(buf, 12, stream_params->user_data);
753
0
    if(bytesRead != 12)
754
0
      return nullptr;
755
0
    stream_params->seek_fn(0, stream_params->user_data);
756
0
    GRK_CODEC_FORMAT fmt;
757
0
    if(!grk_decompress_buffer_detect_format(buf, 12, &fmt))
758
0
    {
759
0
      grklog.error("Unable to detect codec format.");
760
0
      return nullptr;
761
0
    }
762
0
    bstream->setFormat(fmt);
763
0
  }
764
765
0
  grk_stream_set_user_data(stream, stream_params->user_data, stream_params->free_user_data_fn);
766
0
  if(readStream)
767
0
    grk_stream_set_user_data_length(stream, stream_params->stream_len);
768
0
  grk_stream_set_read_function(stream, stream_params->read_fn);
769
0
  grk_stream_set_write_function(stream, stream_params->write_fn);
770
0
  grk_stream_set_seek_function(stream, stream_params->seek_fn);
771
772
0
  return stream;
773
0
}
774
775
static grk_stream* grk_stream_create_file_stream(const char* fname, size_t buffer_size,
776
                                                 bool is_read_stream)
777
0
{
778
0
  bool stdin_stdout = !fname || !fname[0];
779
0
  FILE* file = nullptr;
780
0
  if(stdin_stdout)
781
0
  {
782
0
    file = is_read_stream ? stdin : stdout;
783
0
  }
784
0
  else
785
0
  {
786
0
    const char* mode = (is_read_stream) ? "rb" : "wb";
787
0
    file = fopen(fname, mode);
788
0
    if(!file)
789
0
      return nullptr;
790
0
  }
791
0
  auto stream = grk_stream_new(buffer_size, is_read_stream);
792
0
  if(!stream)
793
0
  {
794
0
    if(!stdin_stdout)
795
0
      fclose(file);
796
0
    return nullptr;
797
0
  }
798
  // validate
799
0
  if(is_read_stream)
800
0
  {
801
0
    uint8_t buf[12];
802
0
    size_t bytesRead = fread(buf, 1, 12, file);
803
0
    if(bytesRead != 12)
804
0
      return nullptr;
805
0
    rewind(file);
806
0
    auto bstream = BufferedStream::getImpl(stream);
807
0
    GRK_CODEC_FORMAT fmt;
808
0
    if(!grk_decompress_buffer_detect_format(buf, 12, &fmt))
809
0
    {
810
0
      grklog.error("Unable to detect codec format.");
811
0
      return nullptr;
812
0
    }
813
0
    bstream->setFormat(fmt);
814
0
  }
815
816
0
  grk_stream_set_user_data(stream, file, stdin_stdout ? nullptr : grkFree_file);
817
0
  if(is_read_stream)
818
0
    grk_stream_set_user_data_length(stream, grk_get_data_length_from_file(file));
819
0
  grk_stream_set_read_function(stream, grk_read_from_file);
820
0
  grk_stream_set_write_function(stream, grk_write_to_file);
821
0
  grk_stream_set_seek_function(stream, grk_seek_in_file);
822
0
  return stream;
823
0
}
824
825
/**********************************************************************
826
 Plugin interface implementation
827
 ***********************************************************************/
828
829
static const char* plugin_get_debug_state_method_name = "plugin_get_debug_state";
830
static const char* plugin_init_method_name = "plugin_init";
831
static const char* plugin_encode_method_name = "plugin_encode";
832
static const char* plugin_batch_encode_method_name = "plugin_batch_encode";
833
static const char* plugin_stop_batch_encode_method_name = "plugin_stop_batch_encode";
834
static const char* plugin_wait_for_batch_complete_method_name = "plugin_wait_for_batch_complete";
835
static const char* plugin_decode_method_name = "plugin_decompress";
836
static const char* plugin_init_batch_decode_method_name = "plugin_init_batch_decompress";
837
static const char* plugin_batch_decode_method_name = "plugin_batch_decompress";
838
static const char* plugin_stop_batch_decode_method_name = "plugin_stop_batch_decompress";
839
840
static const char* pathSeparator()
841
0
{
842
#ifdef _WIN32
843
  return "\\";
844
#else
845
0
  return "/";
846
0
#endif
847
0
}
848
849
bool pluginLoaded = false;
850
bool GRK_CALLCONV grk_plugin_load(grk_plugin_load_info info)
851
2
{
852
2
  if(!info.pluginPath)
853
2
    return false;
854
855
  // form plugin name
856
0
  std::string pluginName = "";
857
0
#if !defined(_WIN32)
858
0
  pluginName += "lib";
859
0
#endif
860
0
  pluginName += std::string(GROK_PLUGIN_NAME) + "." + minpf_get_dynamic_library_extension();
861
862
  // form absolute plugin path
863
0
  auto pluginPath = std::string(info.pluginPath) + pathSeparator() + pluginName;
864
0
  int32_t rc = minpf_load_from_path(pluginPath.c_str(), info.verbose, nullptr);
865
866
  // if fails, try local path
867
0
  if(rc)
868
0
  {
869
0
    std::string localPlugin = std::string(".") + pathSeparator() + pluginName;
870
0
    rc = minpf_load_from_path(localPlugin.c_str(), info.verbose, nullptr);
871
0
  }
872
0
  pluginLoaded = !rc;
873
0
  if(!pluginLoaded)
874
0
    minpf_cleanup_plugin_manager();
875
0
  return pluginLoaded;
876
2
}
877
uint32_t GRK_CALLCONV grk_plugin_get_debug_state()
878
10.5k
{
879
10.5k
  uint32_t rc = GRK_PLUGIN_STATE_NO_DEBUG;
880
10.5k
  if(!pluginLoaded)
881
10.5k
    return rc;
882
0
  auto mgr = minpf_get_plugin_manager();
883
0
  if(mgr && mgr->num_libraries > 0)
884
0
  {
885
0
    auto func = (PLUGIN_GET_DEBUG_STATE)minpf_get_symbol(mgr->dynamic_libraries[0],
886
0
                                                         plugin_get_debug_state_method_name);
887
0
    if(func)
888
0
      rc = func();
889
0
  }
890
0
  return rc;
891
10.5k
}
892
void GRK_CALLCONV grk_plugin_cleanup(void)
893
0
{
894
0
  minpf_cleanup_plugin_manager();
895
0
  pluginLoaded = false;
896
0
}
897
GRK_API bool GRK_CALLCONV grk_plugin_init(grk_plugin_init_info initInfo)
898
0
{
899
0
  if(!pluginLoaded)
900
0
    return false;
901
0
  auto mgr = minpf_get_plugin_manager();
902
0
  if(mgr && mgr->num_libraries > 0)
903
0
  {
904
0
    auto func = (PLUGIN_INIT)minpf_get_symbol(mgr->dynamic_libraries[0], plugin_init_method_name);
905
0
    if(func)
906
0
      return func(initInfo);
907
0
  }
908
0
  return false;
909
0
}
910
911
/*******************
912
 Encode Implementation
913
 ********************/
914
915
GRK_PLUGIN_COMPRESS_USER_CALLBACK userEncodeCallback = 0;
916
917
/* wrapper for user's compress callback */
918
uint64_t grk_plugin_internal_encode_callback(grk_plugin_compress_user_callback_info* info)
919
0
{
920
0
  uint64_t rc = 0;
921
0
  if(userEncodeCallback)
922
0
    rc = userEncodeCallback(info);
923
924
0
  return rc;
925
0
}
926
int32_t GRK_CALLCONV grk_plugin_compress(grk_cparameters* compress_parameters,
927
                                         GRK_PLUGIN_COMPRESS_USER_CALLBACK callback)
928
0
{
929
0
  if(!pluginLoaded)
930
0
    return -1;
931
0
  userEncodeCallback = callback;
932
0
  auto mgr = minpf_get_plugin_manager();
933
0
  if(mgr && mgr->num_libraries > 0)
934
0
  {
935
0
    auto func =
936
0
        (PLUGIN_ENCODE)minpf_get_symbol(mgr->dynamic_libraries[0], plugin_encode_method_name);
937
0
    if(func)
938
0
      return func((grk_cparameters*)compress_parameters, grk_plugin_internal_encode_callback);
939
0
  }
940
0
  return -1;
941
0
}
942
int32_t GRK_CALLCONV grk_plugin_batch_compress(grk_plugin_compress_batch_info info)
943
0
{
944
0
  if(!pluginLoaded)
945
0
    return -1;
946
0
  userEncodeCallback = info.callback;
947
0
  auto mgr = minpf_get_plugin_manager();
948
0
  info.callback = grk_plugin_internal_encode_callback;
949
0
  if(mgr && mgr->num_libraries > 0)
950
0
  {
951
0
    auto func = (PLUGIN_BATCH_ENCODE)minpf_get_symbol(mgr->dynamic_libraries[0],
952
0
                                                      plugin_batch_encode_method_name);
953
0
    if(func)
954
0
      return func(info);
955
0
  }
956
0
  return -1;
957
0
}
958
959
PLUGIN_WAIT_FOR_BATCH_COMPLETE funcPluginWaitForBatchComplete = nullptr;
960
GRK_API void GRK_CALLCONV grk_plugin_wait_for_batch_complete(void)
961
0
{
962
0
  if(!pluginLoaded)
963
0
    return;
964
0
  auto mgr = minpf_get_plugin_manager();
965
0
  if(mgr && mgr->num_libraries > 0)
966
0
  {
967
0
    if(!funcPluginWaitForBatchComplete)
968
0
      funcPluginWaitForBatchComplete = (PLUGIN_WAIT_FOR_BATCH_COMPLETE)minpf_get_symbol(
969
0
          mgr->dynamic_libraries[0], plugin_wait_for_batch_complete_method_name);
970
0
    if(funcPluginWaitForBatchComplete)
971
0
      funcPluginWaitForBatchComplete();
972
0
  }
973
0
}
974
void GRK_CALLCONV grk_plugin_stop_batch_compress(void)
975
0
{
976
0
  if(!pluginLoaded)
977
0
    return;
978
0
  auto mgr = minpf_get_plugin_manager();
979
0
  if(mgr && mgr->num_libraries > 0)
980
0
  {
981
0
    auto func = (PLUGIN_STOP_BATCH_ENCODE)minpf_get_symbol(mgr->dynamic_libraries[0],
982
0
                                                           plugin_stop_batch_encode_method_name);
983
0
    if(func)
984
0
      func();
985
0
  }
986
0
}
987
988
/*******************
989
 Decompress Implementation
990
 ********************/
991
992
grk_plugin_decompress_callback decodeCallback = 0;
993
994
/* wrapper for user's decompress callback */
995
int32_t grk_plugin_internal_decode_callback(PluginDecodeCallbackInfo* info)
996
0
{
997
0
  int32_t rc = -1;
998
  /* set code block data etc on code object */
999
0
  grk_plugin_decompress_callback_info grokInfo;
1000
0
  memset(&grokInfo, 0, sizeof(grk_plugin_decompress_callback_info));
1001
0
  grokInfo.init_decompressors_func = info->init_decompressors_func;
1002
0
  grokInfo.input_file_name = info->inputFile.empty() ? nullptr : info->inputFile.c_str();
1003
0
  grokInfo.output_file_name = info->outputFile.empty() ? nullptr : info->outputFile.c_str();
1004
0
  grokInfo.decod_format = info->decod_format;
1005
0
  grokInfo.cod_format = info->cod_format;
1006
0
  grokInfo.decompressor_parameters = info->decompressor_parameters;
1007
0
  grokInfo.codec = info->codec;
1008
0
  grokInfo.image = info->image;
1009
0
  grokInfo.plugin_owns_image = info->plugin_owns_image;
1010
0
  grokInfo.tile = info->tile;
1011
0
  grokInfo.decompress_flags = info->decompress_flags;
1012
0
  grokInfo.user_data = info->decompressor_parameters->user_data;
1013
0
  if(decodeCallback)
1014
0
    rc = decodeCallback(&grokInfo);
1015
  // synch
1016
0
  info->image = grokInfo.image;
1017
0
  info->codec = grokInfo.codec;
1018
0
  info->header_info = grokInfo.header_info;
1019
0
  return rc;
1020
0
}
1021
1022
int32_t GRK_CALLCONV grk_plugin_decompress(grk_decompress_parameters* decompress_parameters,
1023
                                           grk_plugin_decompress_callback callback)
1024
0
{
1025
0
  if(!pluginLoaded)
1026
0
    return -1;
1027
0
  decodeCallback = callback;
1028
0
  auto mgr = minpf_get_plugin_manager();
1029
0
  if(mgr && mgr->num_libraries > 0)
1030
0
  {
1031
0
    auto func =
1032
0
        (PLUGIN_DECODE)minpf_get_symbol(mgr->dynamic_libraries[0], plugin_decode_method_name);
1033
0
    if(func)
1034
0
      return func((grk_decompress_parameters*)decompress_parameters,
1035
0
                  grk_plugin_internal_decode_callback);
1036
0
  }
1037
0
  return -1;
1038
0
}
1039
int32_t GRK_CALLCONV grk_plugin_init_batch_decompress(
1040
    const char* input_dir, const char* output_dir, grk_decompress_parameters* decompress_parameters,
1041
    grk_plugin_decompress_callback callback)
1042
0
{
1043
0
  if(!pluginLoaded)
1044
0
    return -1;
1045
0
  decodeCallback = callback;
1046
0
  auto mgr = minpf_get_plugin_manager();
1047
0
  if(mgr && mgr->num_libraries > 0)
1048
0
  {
1049
0
    auto func = (PLUGIN_INIT_BATCH_DECODE)minpf_get_symbol(mgr->dynamic_libraries[0],
1050
0
                                                           plugin_init_batch_decode_method_name);
1051
0
    if(func)
1052
0
      return func(input_dir, output_dir, (grk_decompress_parameters*)decompress_parameters,
1053
0
                  grk_plugin_internal_decode_callback);
1054
0
  }
1055
0
  return -1;
1056
0
}
1057
int32_t GRK_CALLCONV grk_plugin_batch_decompress(void)
1058
0
{
1059
0
  if(!pluginLoaded)
1060
0
    return -1;
1061
0
  auto mgr = minpf_get_plugin_manager();
1062
0
  if(mgr && mgr->num_libraries > 0)
1063
0
  {
1064
0
    auto func = (PLUGIN_BATCH_DECODE)minpf_get_symbol(mgr->dynamic_libraries[0],
1065
0
                                                      plugin_batch_decode_method_name);
1066
0
    if(func)
1067
0
      return func();
1068
0
  }
1069
0
  return -1;
1070
0
}
1071
void GRK_CALLCONV grk_plugin_stop_batch_decompress(void)
1072
0
{
1073
0
  if(!pluginLoaded)
1074
0
    return;
1075
0
  auto mgr = minpf_get_plugin_manager();
1076
0
  if(mgr && mgr->num_libraries > 0)
1077
0
  {
1078
0
    auto func = (PLUGIN_STOP_BATCH_DECODE)minpf_get_symbol(mgr->dynamic_libraries[0],
1079
0
                                                           plugin_stop_batch_decode_method_name);
1080
0
    if(func)
1081
0
      func();
1082
0
  }
1083
0
}
1084
1085
void grk_stream_set_read_function(grk_stream* stream, grk_stream_read_fn func)
1086
752
{
1087
752
  auto streamImpl = BufferedStream::getImpl(stream);
1088
752
  if((!streamImpl) || (!(streamImpl->getStatus() & GROK_STREAM_STATUS_INPUT)))
1089
0
    return;
1090
752
  streamImpl->setReadFunction(func);
1091
752
}
1092
1093
void grk_stream_set_seek_function(grk_stream* stream, grk_stream_seek_fn func)
1094
752
{
1095
752
  auto streamImpl = BufferedStream::getImpl(stream);
1096
752
  if(streamImpl)
1097
752
    streamImpl->setSeekFunction(func);
1098
752
}
1099
void grk_stream_set_write_function(grk_stream* stream, grk_stream_write_fn func)
1100
0
{
1101
0
  auto streamImpl = BufferedStream::getImpl(stream);
1102
0
  if((!streamImpl) || (!(streamImpl->getStatus() & GROK_STREAM_STATUS_OUTPUT)))
1103
0
    return;
1104
1105
0
  streamImpl->setWriteFunction(func);
1106
0
}
1107
1108
void grk_stream_set_user_data(grk_stream* stream, void* p_data, grk_stream_free_user_data_fn func)
1109
752
{
1110
752
  auto streamImpl = BufferedStream::getImpl(stream);
1111
752
  if(!streamImpl)
1112
0
    return;
1113
752
  streamImpl->setUserData(p_data, func);
1114
752
}
1115
void grk_stream_set_user_data_length(grk_stream* stream, uint64_t data_length)
1116
752
{
1117
752
  auto streamImpl = BufferedStream::getImpl(stream);
1118
752
  if(streamImpl)
1119
752
    streamImpl->setUserDataLength(data_length);
1120
752
}