OpenJPH
Open-source implementation of JPEG2000 Part-15
Loading...
Searching...
No Matches
ojph_codestream_local.cpp
Go to the documentation of this file.
1//***************************************************************************/
2// This software is released under the 2-Clause BSD license, included
3// below.
4//
5// Copyright (c) 2019, Aous Naman
6// Copyright (c) 2019, Kakadu Software Pty Ltd, Australia
7// Copyright (c) 2019, The University of New South Wales, Australia
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13// 1. Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15//
16// 2. Redistributions in binary form must reproduce the above copyright
17// notice, this list of conditions and the following disclaimer in the
18// documentation and/or other materials provided with the distribution.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
26// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31//***************************************************************************/
32// This file is part of the OpenJPH software implementation.
33// File: ojph_codestream_local.cpp
34// Author: Aous Naman
35// Date: 28 August 2019
36//***************************************************************************/
37
38
39#include <climits>
40#include <cmath>
41
42#include "ojph_mem.h"
43#include "ojph_params.h"
45#include "ojph_tile.h"
46
49
50namespace ojph {
51
52 namespace local
53 {
54
67
70 {
71 if (allocator)
72 delete allocator;
73 if (elastic_alloc)
74 delete elastic_alloc;
75 }
76
79 {
80 tiles = NULL;
81 lines = NULL;
82 comp_size = NULL;
83 recon_comp_size = NULL;
84 outfile = NULL;
85 infile = NULL;
86
87 num_comps = 0;
89 planar = -1;
92 need_tlm = false;
93
94 cur_comp = 0;
95 cur_line = 0;
96 cur_tile_row = 0;
97 resilient = false;
99
101
102 cod.restart();
103 qcd.restart();
104 nlt.restart();
105 dfs.restart();
106 atk.restart();
107
108 allocator->restart();
109 elastic_alloc->restart();
110 }
111
114 {
120 if (num_tiles.area() > 65535)
121 OJPH_ERROR(0x00030011, "the number of tiles cannot exceed 65535");
122 if (num_tiles.area() == 0)
123 OJPH_ERROR(0x00030012, "the number of tiles cannot be 0");
124
125 //allocate tiles
126 allocator->pre_alloc_obj<tile>((size_t)num_tiles.area());
127
128 ui32 num_tileparts = 0;
129 point index;
130 rect tile_rect, recon_tile_rect;
131 ui32 ds = 1 << skipped_res_for_recon;
132 for (index.y = 0; index.y < num_tiles.h; ++index.y)
133 {
134 ui32 y0 = sz.get_tile_offset().y
135 + index.y * sz.get_tile_size().h;
136 ui32 y1 = y0 + sz.get_tile_size().h; //end of tile
137
138 tile_rect.org.y = ojph_max(y0, sz.get_image_offset().y);
139 tile_rect.siz.h =
140 ojph_min(y1, sz.get_image_extent().y) - tile_rect.org.y;
141
142 recon_tile_rect.org.y = ojph_max(ojph_div_ceil(y0, ds),
144 recon_tile_rect.siz.h = ojph_min(ojph_div_ceil(y1, ds),
146 - recon_tile_rect.org.y;
147
148 for (index.x = 0; index.x < num_tiles.w; ++index.x)
149 {
150 ui32 x0 = sz.get_tile_offset().x
151 + index.x * sz.get_tile_size().w;
152 ui32 x1 = x0 + sz.get_tile_size().w;
153
154 tile_rect.org.x = ojph_max(x0, sz.get_image_offset().x);
155 tile_rect.siz.w =
156 ojph_min(x1, sz.get_image_extent().x) - tile_rect.org.x;
157
158 recon_tile_rect.org.x = ojph_max(ojph_div_ceil(x0, ds),
160 recon_tile_rect.siz.w = ojph_min(ojph_div_ceil(x1, ds),
162 - recon_tile_rect.org.x;
163
164 ui32 tps = 0; // number of tileparts for this tile
165 tile::pre_alloc(this, tile_rect, recon_tile_rect, tps);
166 num_tileparts += tps;
167 }
168 }
169
170 //allocate lines
171 //These lines are used by codestream to exchange data with external
172 // world
174 allocator->pre_alloc_obj<line_buf>(num_comps);
175 allocator->pre_alloc_obj<size>(num_comps); //for *comp_size
176 allocator->pre_alloc_obj<size>(num_comps); //for *recon_comp_size
177 for (ui32 i = 0; i < num_comps; ++i)
178 allocator->pre_alloc_data<si32>(siz.get_recon_width(i), 0);
179
180 //allocate tlm
181 if (outfile != NULL && need_tlm)
182 allocator->pre_alloc_obj<param_tlm::Ttlm_Ptlm_pair>(num_tileparts);
183
184 //precinct scratch buffer
185 ui32 num_decomps = cod.get_num_decompositions();
186 size log_cb = cod.get_log_block_dims();
187
188 size ratio;
189 for (ui32 r = 0; r <= num_decomps; ++r)
190 {
191 size log_PP = cod.get_log_precinct_size(r);
192 ratio.w = ojph_max(ratio.w, log_PP.w - ojph_min(log_cb.w, log_PP.w));
193 ratio.h = ojph_max(ratio.h, log_PP.h - ojph_min(log_cb.h, log_PP.h));
194 }
195 ui32 max_ratio = ojph_max(ratio.w, ratio.h);
196 max_ratio = 1 << max_ratio;
197 // assuming that we have a hierarchy of n levels.
198 // This needs 4/3 times the area, rounded up
199 // (rounding up leaves one extra entry).
200 // This exta entry is necessary
201 // We need 4 such tables. These tables store
202 // 1. missing msbs and 2. their flags,
203 // 3. number of layers and 4. their flags
205 4 * ((max_ratio * max_ratio * 4 + 2) / 3);
206
208 }
209
212 {
213 allocator->alloc();
214
215 //precinct scratch buffer
218
219 //get tiles
220 tiles = this->allocator->post_alloc_obj<tile>((size_t)num_tiles.area());
221
222 ui32 num_tileparts = 0;
223 point index;
224 rect tile_rect;
226 for (index.y = 0; index.y < num_tiles.h; ++index.y)
227 {
228 ui32 y0 = sz.get_tile_offset().y
229 + index.y * sz.get_tile_size().h;
230 ui32 y1 = y0 + sz.get_tile_size().h; //end of tile
231
232 tile_rect.org.y = ojph_max(y0, sz.get_image_offset().y);
233 tile_rect.siz.h =
234 ojph_min(y1, sz.get_image_extent().y) - tile_rect.org.y;
235
236 ui32 offset = 0;
237 for (index.x = 0; index.x < num_tiles.w; ++index.x)
238 {
239 ui32 x0 = sz.get_tile_offset().x
240 + index.x * sz.get_tile_size().w;
241 ui32 x1 = x0 + sz.get_tile_size().w;
242
243 tile_rect.org.x = ojph_max(x0, sz.get_image_offset().x);
244 tile_rect.siz.w =
245 ojph_min(x1, sz.get_image_extent().x) - tile_rect.org.x;
246
247 ui32 tps = 0; // number of tileparts for this tile
248 ui32 idx = index.y * num_tiles.w + index.x;
249 tiles[idx].finalize_alloc(this, tile_rect, idx, offset, tps);
250 num_tileparts += tps;
251 }
252 }
253
254 //allocate lines
255 //These lines are used by codestream to exchange data with external
256 // world
257 this->num_comps = sz.get_num_components();
258 lines = allocator->post_alloc_obj<line_buf>(this->num_comps);
259 comp_size = allocator->post_alloc_obj<size>(this->num_comps);
260 recon_comp_size = allocator->post_alloc_obj<size>(this->num_comps);
261 employ_color_transform = cod.is_employing_color_transform();
262 for (ui32 i = 0; i < this->num_comps; ++i)
263 {
264 comp_size[i].w = siz.get_width(i);
265 comp_size[i].h = siz.get_height(i);
266 ui32 cw = siz.get_recon_width(i);
267 recon_comp_size[i].w = cw;
268 recon_comp_size[i].h = siz.get_recon_height(i);
269 lines[i].wrap(allocator->post_alloc_data<si32>(cw, 0), cw, 0);
270 }
271
272 cur_comp = 0;
273 cur_line = 0;
274
275 //allocate tlm
276 if (outfile != NULL && need_tlm)
277 tlm.init(num_tileparts,
278 allocator->post_alloc_obj<param_tlm::Ttlm_Ptlm_pair>(num_tileparts));
279 }
280
281
284 {
285 //two possibilities lossy single tile or lossless
286 //For the following code, we use the least strict profile
287 ojph::param_siz sz(&siz);
288 ojph::param_cod cd(&cod);
289 bool reversible = cd.is_reversible();
290 bool imf2k = !reversible, imf4k = !reversible, imf8k = !reversible;
291 bool imf2kls = reversible, imf4kls = reversible, imf8kls = reversible;
292
293 if (reversible)
294 {
295 point ext = sz.get_image_extent();
296 if (ext.x <= 2048 && ext.y <= 1556)
297 imf2kls &= true;
298 if (ext.x <= 4096 && ext.y <= 3112)
299 imf4kls &= true;
300 if (ext.x <= 8192 && ext.y <= 6224)
301 imf8kls &= true;
302
303 if (!imf2kls && !imf4kls && !imf8kls)
304 OJPH_ERROR(0x000300C1,
305 "Image dimensions do not meet any of the lossless IMF profiles");
306 }
307 else
308 {
309 point ext = sz.get_image_extent();
310 if (ext.x <= 2048 && ext.y <= 1556)
311 imf2k &= true;
312 if (ext.x <= 4096 && ext.y <= 3112)
313 imf4k &= true;
314 if (ext.x <= 8192 && ext.y <= 6224)
315 imf8k &= true;
316
317 if (!imf2k && !imf4k && !imf8k)
318 OJPH_ERROR(0x000300C2,
319 "Image dimensions do not meet any of the lossy IMF profiles");
320 }
321
322
323 if (sz.get_image_offset().x != 0 || sz.get_image_offset().y != 0)
324 OJPH_ERROR(0x000300C3,
325 "For IMF profile, image offset (XOsiz, YOsiz) has to be 0.");
326 if (sz.get_tile_offset().x != 0 || sz.get_tile_offset().y != 0)
327 OJPH_ERROR(0x000300C4,
328 "For IMF profile, tile offset (XTOsiz, YTOsiz) has to be 0.");
329 if (sz.get_num_components() > 3)
330 OJPH_ERROR(0x000300C5,
331 "For IMF profile, the number of components has to be less "
332 " or equal to 3");
333 bool test_ds1 = true, test_ds2 = true;
334 for (ojph::ui32 i = 0; i < sz.get_num_components(); ++i)
335 {
336 point downsamping = sz.get_downsampling(i);
337 test_ds1 &= downsamping.y == 1;
338 test_ds2 &= downsamping.y == 1;
339
340 test_ds1 &= downsamping.x == 1;
341 if (i == 1 || i == 2)
342 test_ds2 &= downsamping.x == 2;
343 else
344 test_ds2 &= downsamping.x == 1;
345 }
346 if (!test_ds1 && !test_ds2)
347 OJPH_ERROR(0x000300C6,
348 "For IMF profile, either no component downsampling is used,"
349 " or the x-dimension of the 2nd and 3rd components is downsampled"
350 " by 2.");
351
352 bool test_bd = true;
353 for (ojph::ui32 i = 0; i < sz.get_num_components(); ++i)
354 {
355 ui32 bit_depth = sz.get_bit_depth(i);
356 bool is_signed = sz.is_signed(i);
357 test_bd &= bit_depth >= 8 && bit_depth <= 16 && is_signed == false;
358 }
359 if (!test_bd)
360 OJPH_ERROR(0x000300C7,
361 "For IMF profile, compnent bit_depth has to be between"
362 " 8 and 16 bits inclusively, and the samples must be unsigned");
363
364 if (cd.get_log_block_dims().w != 5 || cd.get_log_block_dims().h != 5)
365 OJPH_ERROR(0x000300C8,
366 "For IMF profile, codeblock dimensions are restricted."
367 " Use \"-block_size {32,32}\" at the commandline");
368
369 ui32 num_decomps = cd.get_num_decompositions();
370 bool test_pz = cd.get_log_precinct_size(0).w == 7
371 && cd.get_log_precinct_size(0).h == 7;
372 for (ui32 i = 1; i <= num_decomps; ++i)
373 test_pz = cd.get_log_precinct_size(i).w == 8
374 && cd.get_log_precinct_size(i).h == 8;
375 if (!test_pz)
376 OJPH_ERROR(0x000300C9,
377 "For IMF profile, precinct sizes are restricted."
378 " Use \"-precincts {128,128},{256,256}\" at the commandline");
379
381 OJPH_ERROR(0x000300CA,
382 "For IMF profile, the CPRL progression order must be used."
383 " Use \"-prog_order CPRL\".");
384
385 imf2k &= num_decomps <= 5;
386 imf2kls &= num_decomps <= 5;
387 imf4k &= num_decomps <= 6;
388 imf4kls &= num_decomps <= 6;
389 imf8k &= num_decomps <= 7;
390 imf8kls &= num_decomps <= 7;
391
392 if (num_decomps == 0 ||
393 (!imf2k && !imf4k && !imf8k && !imf2kls && !imf4kls && !imf8kls))
394 OJPH_ERROR(0x000300CB,
395 "Number of decompositions does not match the IMF profile"
396 " dictated by wavelet reversibility and image dimensions.");
397
398 ui32 tiles_w = sz.get_image_extent().x;
399 tiles_w = ojph_div_ceil(tiles_w, sz.get_tile_size().w);
400 ui32 tiles_h = sz.get_image_extent().y;
401 tiles_h = ojph_div_ceil(tiles_h, sz.get_tile_size().h);
402 ui32 total_tiles = tiles_w * tiles_h;
403
404 if (total_tiles > 1)
405 {
406 if (!reversible)
407 OJPH_ERROR(0x000300CC,
408 "Lossy IMF profile must have one tile.");
409
410 size tt = sz.get_tile_size();
411 imf2kls &= (tt.w == 1024 && tt.h == 1024);
412 imf2kls &= (tt.w >= 1024 && num_decomps <= 4)
413 || (tt.w >= 2048 && num_decomps <= 5);
414 imf4kls &= (tt.w == 1024 && tt.h == 1024)
415 || (tt.w == 2048 && tt.h == 2048);
416 imf4kls &= (tt.w >= 1024 && num_decomps <= 4)
417 || (tt.w >= 2048 && num_decomps <= 5)
418 || (tt.w >= 4096 && num_decomps <= 6);
419 imf8kls &= (tt.w == 1024 && tt.h == 1024)
420 || (tt.w == 2048 && tt.h == 2048)
421 || (tt.w == 4096 && tt.h == 4096);
422 imf8kls &= (tt.w >= 1024 && num_decomps <= 4)
423 || (tt.w >= 2048 && num_decomps <= 5)
424 || (tt.w >= 4096 && num_decomps <= 6)
425 || (tt.w >= 8192 && num_decomps <= 7);
426 if (!imf2kls && !imf4kls && !imf8kls)
427 OJPH_ERROR(0x000300CD,
428 "Number of decompositions does not match the IMF profile"
429 " dictated by wavelet reversibility and image dimensions and"
430 " tiles.");
431 }
432
433 need_tlm = true;
436 {
438 OJPH_WARN(0x000300C1,
439 "In IMF profile, tile part divisions at the component level must be "
440 "employed, while at the resolution level is not allowed. "
441 "This has been corrected.");
442 }
443 }
444
447 {
448 ojph::param_siz sz(&siz);
449 ojph::param_cod cd(&cod);
450
451 if (sz.get_image_offset().x != 0 || sz.get_image_offset().y != 0)
452 OJPH_ERROR(0x000300B1,
453 "For broadcast profile, image offset (XOsiz, YOsiz) has to be 0.");
454 if (sz.get_tile_offset().x != 0 || sz.get_tile_offset().y != 0)
455 OJPH_ERROR(0x000300B2,
456 "For broadcast profile, tile offset (XTOsiz, YTOsiz) has to be 0.");
457 if (sz.get_num_components() > 4)
458 OJPH_ERROR(0x000300B3,
459 "For broadcast profile, the number of components has to be less "
460 " or equal to 4");
461 bool test_ds1 = true, test_ds2 = true;
462 for (ojph::ui32 i = 0; i < sz.get_num_components(); ++i)
463 {
464 point downsamping = sz.get_downsampling(i);
465 test_ds1 &= downsamping.y == 1;
466 test_ds2 &= downsamping.y == 1;
467
468 test_ds1 &= downsamping.x == 1;
469 if (i == 1 || i == 2)
470 test_ds2 &= downsamping.x == 2;
471 else
472 test_ds2 &= downsamping.x == 1;
473 }
474 if (!test_ds1 && !test_ds2)
475 OJPH_ERROR(0x000300B4,
476 "For broadcast profile, either no component downsampling is used,"
477 " or the x-dimension of the 2nd and 3rd components is downsampled"
478 " by 2.");
479
480 bool test_bd = true;
481 for (ojph::ui32 i = 0; i < sz.get_num_components(); ++i)
482 {
483 ui32 bit_depth = sz.get_bit_depth(i);
484 bool is_signed = sz.is_signed(i);
485 test_bd &= bit_depth >= 8 && bit_depth <= 12 && is_signed == false;
486 }
487 if (!test_bd)
488 OJPH_ERROR(0x000300B5,
489 "For broadcast profile, compnent bit_depth has to be between"
490 " 8 and 12 bits inclusively, and the samples must be unsigned");
491
492 ui32 num_decomps = cd.get_num_decompositions();
493 if (num_decomps == 0 || num_decomps > 5)
494 OJPH_ERROR(0x000300B6,
495 "For broadcast profile, number of decompositions has to be between"
496 "1 and 5 inclusively.");
497
498 if (cd.get_log_block_dims().w < 5 || cd.get_log_block_dims().w > 7)
499 OJPH_ERROR(0x000300B7,
500 "For broadcast profile, codeblock dimensions are restricted such"
501 " that codeblock width has to be either 32, 64, or 128.");
502
503 if (cd.get_log_block_dims().h < 5 || cd.get_log_block_dims().h > 7)
504 OJPH_ERROR(0x000300B8,
505 "For broadcast profile, codeblock dimensions are restricted such"
506 " that codeblock height has to be either 32, 64, or 128.");
507
508 bool test_pz = cd.get_log_precinct_size(0).w == 7
509 && cd.get_log_precinct_size(0).h == 7;
510 for (ui32 i = 1; i <= num_decomps; ++i)
511 test_pz = cd.get_log_precinct_size(i).w == 8
512 && cd.get_log_precinct_size(i).h == 8;
513 if (!test_pz)
514 OJPH_ERROR(0x000300B9,
515 "For broadcast profile, precinct sizes are restricted."
516 " Use \"-precincts {128,128},{256,256}\" at the commandline");
517
519 OJPH_ERROR(0x000300BA,
520 "For broadcast profile, the CPRL progression order must be used."
521 " Use \"-prog_order CPRL\".");
522
523 ui32 tiles_w = sz.get_image_extent().x;
524 tiles_w = ojph_div_ceil(tiles_w, sz.get_tile_size().w);
525 ui32 tiles_h = sz.get_image_extent().y;
526 tiles_h = ojph_div_ceil(tiles_h, sz.get_tile_size().h);
527 ui32 total_tiles = tiles_w * tiles_h;
528
529 if (total_tiles != 1 && total_tiles != 4)
530 OJPH_ERROR(0x000300BB,
531 "The broadcast profile can only have 1 or 4 tiles");
532
533 need_tlm = true;
536 {
538 OJPH_WARN(0x000300B1,
539 "In BROADCAST profile, tile part divisions at the component level "
540 "must be employed, while at the resolution level is not allowed. "
541 "This has been corrected.");
542 }
543 }
544
547 const comment_exchange* comments,
548 ui32 num_comments)
549 {
550 //finalize
551 siz.check_validity(cod);
552 cod.check_validity(siz);
553 cod.update_atk(&atk);
554 qcd.check_validity(siz, cod);
555 cap.check_validity(cod, qcd);
556 nlt.check_validity(siz);
557 if (profile == OJPH_PN_IMF)
559 else if (profile == OJPH_PN_BROADCAST)
561
563 if ((po == OJPH_PO_LRCP || po == OJPH_PO_RLCP) &&
565 {
567 OJPH_INFO(0x00030021,
568 "For LRCP and RLCP progression orders, tilepart divisions at the "
569 "component level, means that we have a tilepart for every "
570 "resolution and component.\n");
571 }
573 {
575 OJPH_WARN(0x00030021,
576 "For RPCL progression, having tilepart divisions at the component "
577 "level means a tilepart for every precinct, which does not "
578 "make sense, since we can have no more than 255 tile parts. This "
579 "has been corrected by removing tilepart divisions at the component "
580 "level.");
581 }
582 if (po == OJPH_PO_PCRL && tilepart_div != 0)
583 {
584 tilepart_div = 0;
585 OJPH_WARN(0x00030022,
586 "For PCRL progression, having tilepart divisions at the component "
587 "level or the resolution level means a tile part for every "
588 "precinct, which does not make sense, since we can have no more "
589 "than 255 tile parts. This has been corrected by removing tilepart "
590 "divisions; use another progression if you want tileparts.");
591 }
593 {
595 OJPH_WARN(0x00030023,
596 "For CPRL progression, having tilepart divisions at the resolution "
597 "level means a tile part for every precinct, which does not "
598 "make sense, since we can have no more than 255 tile parts. This "
599 "has been corrected by removing tilepart divisions at the "
600 "resolution level.");
601 }
602
603 if (planar == -1) //not initialized
604 planar = cod.is_employing_color_transform() ? 1 : 0;
605 else if (planar == 0) //interleaved is chosen
606 {
607 }
608 else if (planar == 1) //plannar is chosen
609 {
610 if (cod.is_employing_color_transform() == true)
611 OJPH_ERROR(0x00030021,
612 "the planar interface option cannot be used when colour "
613 "transform is employed");
614 }
615 else
616 assert(0);
617
618 assert(this->outfile == NULL);
619 this->outfile = file;
620 this->pre_alloc();
621 this->finalize_alloc();
622
624 if (file->write(&t, 2) != 2)
625 OJPH_ERROR(0x00030022, "Error writing to file");
626
627 if (!siz.write(file))
628 OJPH_ERROR(0x00030023, "Error writing to file");
629
630 if (!cap.write(file))
631 OJPH_ERROR(0x00030024, "Error writing to file");
632
633 if (!cod.write(file))
634 OJPH_ERROR(0x00030025, "Error writing to file");
635
636 if (!cod.write_coc(file, num_comps))
637 OJPH_ERROR(0x0003002E, "Error writing to file");
638
639 if (!qcd.write(file))
640 OJPH_ERROR(0x00030026, "Error writing to file");
641
642 if (!qcd.write_qcc(file, num_comps))
643 OJPH_ERROR(0x0003002D, "Error writing to file");
644
645 if (!nlt.write(file))
646 OJPH_ERROR(0x00030027, "Error writing to file");
647
648 char buf[] = " OpenJPH Ver "
652 size_t len = strlen(buf);
654 *(ui16*)(buf + 2) = swap_byte((ui16)(len - 2));
655 //1 for General use (IS 8859-15:1999 (Latin) values)
656 *(ui16*)(buf + 4) = swap_byte((ui16)(1));
657 if (file->write(buf, len) != len)
658 OJPH_ERROR(0x00030028, "Error writing to file");
659
660 if (comments != NULL) {
661 for (ui32 i = 0; i < num_comments; ++i)
662 {
664 if (file->write(&t, 2) != 2)
665 OJPH_ERROR(0x00030029, "Error writing to file");
666 t = swap_byte((ui16)(comments[i].len + 4));
667 if (file->write(&t, 2) != 2)
668 OJPH_ERROR(0x0003002A, "Error writing to file");
669 //1 for General use (IS 8859-15:1999 (Latin) values)
670 t = swap_byte(comments[i].Rcom);
671 if (file->write(&t, 2) != 2)
672 OJPH_ERROR(0x0003002B, "Error writing to file");
673 if (file->write(comments[i].data, comments[i].len)!=comments[i].len)
674 OJPH_ERROR(0x0003002C, "Error writing to file");
675 }
676 }
677 }
678
680 static
681 int find_marker(infile_base *f, const ui16* char_list, int list_len)
682 {
683 //returns the marker index in char_list, or -1
684 while (!f->eof())
685 {
686 ui8 new_char;
687 size_t num_bytes = f->read(&new_char, 1);
688 if (num_bytes != 1)
689 return -1;
690 if (new_char == 0xFF)
691 {
692 size_t num_bytes = f->read(&new_char, 1);
693
694 if (num_bytes != 1)
695 return -1;
696
697 for (int i = 0; i < list_len; ++i)
698 if (new_char == (char_list[i] & 0xFF))
699 return i;
700 }
701 }
702 return -1;
703 }
704
706 static
707 int skip_marker(infile_base *file, const char *marker,
708 const char *msg, int msg_level, bool resilient)
709 {
710 ojph_unused(marker);
711 ui16 com_len;
712 if (file->read(&com_len, 2) != 2)
713 {
714 if (resilient)
715 return -1;
716 else
717 OJPH_ERROR(0x00030041, "error reading marker");
718 }
719 com_len = swap_byte(com_len);
720 file->seek(com_len - 2, infile_base::OJPH_SEEK_CUR);
721 if (msg != NULL && msg_level != OJPH_MSG_NO_MSG)
722 {
723 if (msg_level == OJPH_MSG_INFO)
724 {
725 OJPH_INFO(0x00030001, "%s", msg);
726 }
727 else if (msg_level == OJPH_MSG_WARN)
728 {
729 OJPH_WARN(0x00030001, "%s", msg);
730 }
731 else if (msg_level == OJPH_MSG_ERROR)
732 {
733 OJPH_ERROR(0x00030001, "%s", msg);
734 }
735 else // there is the option of ALL_MSG but it should not be used here
736 assert(0);
737 }
738 return 0;
739 }
740
743 {
744 ui16 marker_list[20] = { SOC, SIZ, CAP, PRF, CPF, COD, COC, QCD, QCC,
745 RGN, POC, PPM, TLM, PLM, CRG, COM, DFS, ATK, NLT, SOT };
746 find_marker(file, marker_list, 1); //find SOC
747 find_marker(file, marker_list + 1, 1); //find SIZ
748 siz.read(file);
749 int marker_idx = 0;
750 int received_markers = 0; //check that COD, & QCD received
751 while (true)
752 {
753 marker_idx = find_marker(file, marker_list + 2, 18);
754 if (marker_idx == 0)
755 cap.read(file);
756 else if (marker_idx == 1)
757 //Skipping PRF marker segment; this should not cause any issues
758 skip_marker(file, "PRF", NULL, OJPH_MSG_NO_MSG, false);
759 else if (marker_idx == 2)
760 //Skipping CPF marker segment; this should not cause any issues
761 skip_marker(file, "CPF", NULL, OJPH_MSG_NO_MSG, false);
762 else if (marker_idx == 3)
763 {
764 cod.read(file);
765 received_markers |= 1;
767 int num_qlayers = c.get_num_layers();
768 if (num_qlayers != 1)
769 OJPH_ERROR(0x00030053, "The current implementation supports "
770 "1 quality layer only. This codestream has %d quality layers",
771 num_qlayers);
772 }
773 else if (marker_idx == 4)
774 {
775 param_cod* p = cod.add_coc_object(param_cod::OJPH_COD_UNKNOWN);
776 p->read_coc(file, siz.get_num_components(), &cod);
777 if (p->get_comp_idx() >= siz.get_num_components())
778 OJPH_INFO(0x00030056, "The codestream carries a COC marker "
779 "segment for a component indexed by %d, which is more than the "
780 "allowed index number, since the codestream has %d components",
781 p->get_comp_idx(), num_comps);
782 param_cod *q = cod.get_coc(p->get_comp_idx());
783 if (p != q && p->get_comp_idx() == q->get_comp_idx())
784 OJPH_ERROR(0x00030057, "The codestream has two COC marker "
785 "segments for one component of index %d", p->get_comp_idx());
786 }
787 else if (marker_idx == 5)
788 {
789 qcd.read(file);
790 received_markers |= 2;
791 }
792 else if (marker_idx == 6)
793 {
794 param_qcd* p = qcd.add_qcc_object(param_qcd::OJPH_QCD_UNKNOWN);
795 p->read_qcc(file, siz.get_num_components());
796 if (p->get_comp_idx() >= siz.get_num_components())
797 OJPH_ERROR(0x00030054, "The codestream carries a QCC marker "
798 "segment for a component indexed by %d, which is more than the "
799 "allowed index number, since the codestream has %d components",
800 p->get_comp_idx(), num_comps);
801 param_qcd *q = qcd.get_qcc(p->get_comp_idx());
802 if (p != q && p->get_comp_idx() == q->get_comp_idx())
803 OJPH_ERROR(0x00030055, "The codestream has two QCC marker "
804 "segments for one component of index %d", p->get_comp_idx());
805 }
806 else if (marker_idx == 7)
807 skip_marker(file, "RGN", "RGN is not supported yet",
808 OJPH_MSG_WARN, false);
809 else if (marker_idx == 8)
810 skip_marker(file, "POC", "POC is not supported yet",
811 OJPH_MSG_WARN, false);
812 else if (marker_idx == 9)
813 skip_marker(file, "PPM", "PPM is not supported yet",
814 OJPH_MSG_WARN, false);
815 else if (marker_idx == 10)
816 //Skipping TLM marker segment; this should not cause any issues
817 skip_marker(file, "TLM", NULL, OJPH_MSG_NO_MSG, false);
818 else if (marker_idx == 11)
819 //Skipping PLM marker segment; this should not cause any issues
820 skip_marker(file, "PLM", NULL, OJPH_MSG_NO_MSG, false);
821 else if (marker_idx == 12)
822 //Skipping CRG marker segment;
823 skip_marker(file, "CRG", "CRG has been ignored; CRG is related to"
824 " where the Cb and Cr colour components are co-sited or located"
825 " with respect to the Y' luma component. Perhaps, it is better"
826 " to get the individual components and assemble the samples"
827 " according to your needs",
828 OJPH_MSG_INFO, false);
829 else if (marker_idx == 13)
830 skip_marker(file, "COM", NULL, OJPH_MSG_NO_MSG, false);
831 else if (marker_idx == 14)
832 dfs.read(file);
833 else if (marker_idx == 15)
834 atk.read(file);
835 else if (marker_idx == 16)
836 nlt.read(file);
837 else if (marker_idx == 17)
838 break;
839 else
840 OJPH_ERROR(0x00030051, "File ended before finding a tile segment");
841 }
842
843 cod.update_atk(&atk);
844 siz.link(&cod);
845 if (dfs.exists())
846 siz.link(&dfs);
847
848 if (received_markers != 3)
849 OJPH_ERROR(0x00030052, "markers error, COD and QCD are required");
850
851 this->infile = file;
852 planar = cod.is_employing_color_transform() ? 0 : 1;
853 }
854
858 {
860 OJPH_ERROR(0x000300A1,
861 "skipped_resolution for data %d must be equal or smaller than "
862 " skipped_resolution for reconstruction %d\n",
864 if (skipped_res_for_read > cod.get_num_decompositions())
865 OJPH_ERROR(0x000300A2,
866 "skipped_resolution for data %d must be smaller than "
867 " the number of decomposition levels %d\n",
868 skipped_res_for_read, cod.get_num_decompositions());
869
870 this->skipped_res_for_read = skipped_res_for_read;
871 this->skipped_res_for_recon = skipped_res_for_recon;
872 siz.set_skipped_resolutions(skipped_res_for_recon);
873 }
874
877 {
878 if (infile != NULL)
879 OJPH_ERROR(0x000300A3, "Codestream resilience must be enabled before"
880 " reading file headers.\n");
881 this->resilient = true;
882 }
883
886 {
887 this->pre_alloc();
888 this->finalize_alloc();
889
890 while (true)
891 {
892 param_sot sot;
893 if (sot.read(infile, resilient))
894 {
895 ui64 tile_start_location = (ui64)infile->tell();
896 bool skip_tile = false;
897
898 if (sot.get_tile_index() >= (int)num_tiles.area())
899 {
900 if (resilient) {
901 OJPH_INFO(0x00030061, "wrong tile index")
902 skip_tile = true; // skip the faulty tile
903 }
904 else
905 OJPH_ERROR(0x00030061, "wrong tile index")
906 }
907
908 if (!skip_tile)
909 {
910 if (sot.get_tile_part_index())
911 { //tile part
912 if (sot.get_num_tile_parts() &&
914 {
915 if (resilient)
916 OJPH_INFO(0x00030062,
917 "error in tile part number, should be smaller than total"
918 " number of tile parts")
919 else
920 OJPH_ERROR(0x00030062,
921 "error in tile part number, should be smaller than total"
922 " number of tile parts")
923 }
924
925 bool sod_found = false;
926 ui16 other_tile_part_markers[7] = { SOT, POC, PPT, PLT, COM,
927 NLT, SOD };
928 while (true)
929 {
930 int marker_idx = 0;
931 int result = 0;
932 marker_idx = find_marker(infile, other_tile_part_markers+1, 6);
933 if (marker_idx == 0)
934 result = skip_marker(infile, "POC",
935 "POC marker segment in a tile is not supported yet",
937 else if (marker_idx == 1)
938 result = skip_marker(infile, "PPT",
939 "PPT marker segment in a tile is not supported yet",
941 else if (marker_idx == 2)
942 //Skipping PLT marker segment;this should not cause any issues
943 result = skip_marker(infile, "PLT", NULL,
945 else if (marker_idx == 3)
946 result = skip_marker(infile, "COM", NULL,
948 else if (marker_idx == 4)
949 result = skip_marker(infile, "NLT",
950 "NLT marker in tile is not supported yet",
952 else if (marker_idx == 5)
953 {
954 sod_found = true;
955 break;
956 }
957
958 if (marker_idx == -1) //marker not found
959 {
960 if (resilient)
961 OJPH_INFO(0x00030063,
962 "File terminated early before start of data is found"
963 " for tile indexed %d and tile part %d",
965 else
966 OJPH_ERROR(0x00030063,
967 "File terminated early before start of data is found"
968 " for tile indexed %d and tile part %d",
970 break;
971 }
972 if (result == -1) //file terminated during marker seg. skipping
973 {
974 if (resilient)
975 OJPH_INFO(0x00030064,
976 "File terminated during marker segment skipping")
977 else
978 OJPH_ERROR(0x00030064,
979 "File terminated during marker segment skipping")
980 break;
981 }
982 }
983 if (sod_found)
984 tiles[sot.get_tile_index()].parse_tile_header(sot, infile,
985 tile_start_location);
986 }
987 else
988 { //first tile part
989 bool sod_found = false;
990 ui16 first_tile_part_markers[12] = { SOT, COD, COC, QCD, QCC, RGN,
991 POC, PPT, PLT, COM, NLT, SOD };
992 while (true)
993 {
994 int marker_idx = 0;
995 int result = 0;
996 marker_idx = find_marker(infile, first_tile_part_markers+1, 11);
997 if (marker_idx == 0)
998 result = skip_marker(infile, "COD",
999 "COD marker segment in a tile is not supported yet",
1001 else if (marker_idx == 1)
1002 result = skip_marker(infile, "COC",
1003 "COC marker segment in a tile is not supported yet",
1005 else if (marker_idx == 2)
1006 result = skip_marker(infile, "QCD",
1007 "QCD marker segment in a tile is not supported yet",
1009 else if (marker_idx == 3)
1010 result = skip_marker(infile, "QCC",
1011 "QCC marker segment in a tile is not supported yet",
1013 else if (marker_idx == 4)
1014 result = skip_marker(infile, "RGN",
1015 "RGN marker segment in a tile is not supported yet",
1017 else if (marker_idx == 5)
1018 result = skip_marker(infile, "POC",
1019 "POC marker segment in a tile is not supported yet",
1021 else if (marker_idx == 6)
1022 result = skip_marker(infile, "PPT",
1023 "PPT marker segment in a tile is not supported yet",
1025 else if (marker_idx == 7)
1026 //Skipping PLT marker segment;this should not cause any issues
1027 result = skip_marker(infile, "PLT", NULL,
1029 else if (marker_idx == 8)
1030 result = skip_marker(infile, "COM", NULL,
1032 else if (marker_idx == 9)
1033 result = skip_marker(infile, "NLT",
1034 "PPT marker segment in a tile is not supported yet",
1036 else if (marker_idx == 10)
1037 {
1038 sod_found = true;
1039 break;
1040 }
1041
1042 if (marker_idx == -1) //marker not found
1043 {
1044 if (resilient)
1045 OJPH_INFO(0x00030065,
1046 "File terminated early before start of data is found"
1047 " for tile indexed %d and tile part %d",
1049 else
1050 OJPH_ERROR(0x00030065,
1051 "File terminated early before start of data is found"
1052 " for tile indexed %d and tile part %d",
1054 break;
1055 }
1056 if (result == -1) //file terminated during marker seg. skipping
1057 {
1058 if (resilient)
1059 OJPH_INFO(0x00030066,
1060 "File terminated during marker segment skipping")
1061 else
1062 OJPH_ERROR(0x00030066,
1063 "File terminated during marker segment skipping")
1064 break;
1065 }
1066 }
1067 if (sod_found)
1068 tiles[sot.get_tile_index()].parse_tile_header(sot, infile,
1069 tile_start_location);
1070 }
1071 }
1072 }
1073
1074 // check the next marker; either SOT or EOC,
1075 // if something is broken, just an end of file
1076 ui16 next_markers[2] = { SOT, EOC };
1077 int marker_idx = find_marker(infile, next_markers, 2);
1078 if (marker_idx == -1)
1079 {
1080 OJPH_INFO(0x00030067, "File terminated early");
1081 break;
1082 }
1083 else if (marker_idx == 0)
1084 ;
1085 else if (marker_idx == 1)
1086 break;
1087 }
1088 }
1089
1092 {
1093 this->planar = planar;
1094 }
1095
1097 void codestream::set_profile(const char *s)
1098 {
1099 size_t len = strlen(s);
1100 if (len == 9 && strncmp(s, OJPH_PN_STRING_BROADCAST, 9) == 0)
1102 else if (len == 3 && strncmp(s, OJPH_PN_STRING_IMF, 3) == 0)
1104 else
1105 OJPH_ERROR(0x000300A1, "unkownn or unsupported profile");
1106 }
1107
1110 {
1111 tilepart_div = value;
1112 }
1113
1116 {
1117 need_tlm = needed;
1118 }
1119
1122 {
1123 si32 repeat = (si32)num_tiles.area();
1124 for (si32 i = 0; i < repeat; ++i)
1125 tiles[i].prepare_for_flush();
1126 if (need_tlm)
1127 { //write tlm
1128 for (si32 i = 0; i < repeat; ++i)
1129 tiles[i].fill_tlm(&tlm);
1130 tlm.write(outfile);
1131 }
1132 for (si32 i = 0; i < repeat; ++i)
1133 tiles[i].flush(outfile);
1135 if (!outfile->write(&t, 2))
1136 OJPH_ERROR(0x00030071, "Error writing to file");
1137 }
1138
1141 {
1142 if (infile)
1143 infile->close();
1144 if (outfile)
1145 outfile->close();
1146 }
1147
1150 {
1151 if (line)
1152 {
1153 bool success = false;
1154 while (!success)
1155 {
1156 success = true;
1157 for (ui32 i = 0; i < num_tiles.w; ++i)
1158 {
1159 ui32 idx = i + cur_tile_row * num_tiles.w;
1160 if ((success &= tiles[idx].push(line, cur_comp)) == false)
1161 break;
1162 }
1163 cur_tile_row += success == false ? 1 : 0;
1164 if (cur_tile_row >= num_tiles.h)
1165 cur_tile_row = 0;
1166 }
1167
1168 if (planar) //process one component at a time
1169 {
1170 if (++cur_line >= comp_size[cur_comp].h)
1171 {
1172 cur_line = 0;
1173 cur_tile_row = 0;
1174 if (++cur_comp >= num_comps)
1175 {
1176 next_component = 0;
1177 return NULL;
1178 }
1179 }
1180 }
1181 else //process all component for a line
1182 {
1183 if (++cur_comp >= num_comps)
1184 {
1185 cur_comp = 0;
1186 if (++cur_line >= comp_size[cur_comp].h)
1187 {
1188 next_component = 0;
1189 return NULL;
1190 }
1191 }
1192 }
1193 }
1194
1195 next_component = cur_comp;
1196 return this->lines + cur_comp;
1197 }
1198
1201 {
1202 bool success = false;
1203 while (!success)
1204 {
1205 success = true;
1206 for (ui32 i = 0; i < num_tiles.w; ++i)
1207 {
1208 ui32 idx = i + cur_tile_row * num_tiles.w;
1209 if ((success &= tiles[idx].pull(lines + cur_comp, cur_comp)) == false)
1210 break;
1211 }
1212 cur_tile_row += success == false ? 1 : 0;
1213 if (cur_tile_row >= num_tiles.h)
1214 cur_tile_row = 0;
1215 }
1216 comp_num = cur_comp;
1217
1218 if (planar) //process one component at a time
1219 {
1220 if (++cur_line >= recon_comp_size[cur_comp].h)
1221 {
1222 cur_line = 0;
1223 cur_tile_row = 0;
1224 if (cur_comp++ >= num_comps)
1225 {
1226 comp_num = 0;
1227 return NULL;
1228 }
1229 }
1230 }
1231 else //process all component for a line
1232 {
1233 if (++cur_comp >= num_comps)
1234 {
1235 cur_comp = 0;
1236 if (cur_line++ >= recon_comp_size[cur_comp].h)
1237 {
1238 comp_num = 0;
1239 return NULL;
1240 }
1241 }
1242 }
1243
1244 return lines + comp_num;
1245 }
1246
1247 }
1248}
virtual bool eof()=0
virtual size_t read(void *ptr, size_t size)=0
line_buf * exchange(line_buf *line, ui32 &next_component)
void restrict_input_resolution(ui32 skipped_res_for_data, ui32 skipped_res_for_recon)
mem_elastic_allocator * elastic_alloc
mem_fixed_allocator * allocator
void write_headers(outfile_base *file, const comment_exchange *comments, ui32 num_comments)
void read_headers(infile_base *file)
line_buf * pull(ui32 &comp_num)
static void pre_alloc(codestream *codestream, const rect &tile_rect, const rect &recon_tile_rect, ui32 &num_tileparts)
Definition ojph_tile.cpp:56
virtual size_t write(const void *ptr, size_t size)=0
int get_progression_order() const
ui32 get_num_decompositions() const
size get_log_block_dims() const
bool is_reversible() const
size get_log_precinct_size(ui32 level_num) const
int get_num_layers() const
point get_image_extent() const
ui32 get_bit_depth(ui32 comp_num) const
point get_image_offset() const
size get_tile_size() const
point get_downsampling(ui32 comp_num) const
point get_tile_offset() const
bool is_signed(ui32 comp_num) const
ui32 get_num_components() const
static int find_marker(infile_base *f, const ui16 *char_list, int list_len)
static int skip_marker(infile_base *file, const char *marker, const char *msg, int msg_level, bool resilient)
void init_wavelet_transform_functions()
void init_colour_transform_functions()
static ui16 swap_byte(ui16 t)
const char OJPH_PN_STRING_BROADCAST[]
const char OJPH_PN_STRING_IMF[]
uint64_t ui64
Definition ojph_defs.h:56
@ OJPH_MSG_INFO
@ OJPH_MSG_ERROR
@ OJPH_MSG_WARN
@ OJPH_MSG_NO_MSG
uint16_t ui16
Definition ojph_defs.h:52
@ OJPH_TILEPART_RESOLUTIONS
@ OJPH_TILEPART_NO_DIVISIONS
@ OJPH_TILEPART_COMPONENTS
@ OJPH_PN_BROADCAST
@ OJPH_PN_UNDEFINED
int32_t si32
Definition ojph_defs.h:55
uint32_t ui32
Definition ojph_defs.h:54
uint8_t ui8
Definition ojph_defs.h:50
#define ojph_max(a, b)
Definition ojph_defs.h:73
#define OJPH_INT_TO_STRING(I)
Definition ojph_defs.h:61
#define ojph_div_ceil(a, b)
Definition ojph_defs.h:70
#define ojph_min(a, b)
Definition ojph_defs.h:76
#define ojph_unused(x)
Definition ojph_defs.h:78
#define OJPH_INFO(t,...)
MACROs to insert file and line number for info, warning, and error.
#define OJPH_ERROR(t,...)
#define OJPH_WARN(t,...)
#define OPENJPH_VERSION_PATCH
#define OPENJPH_VERSION_MAJOR
#define OPENJPH_VERSION_MINOR
void read_coc(infile_base *file, ui32 num_comps, param_cod *top_cod)
void read_qcc(infile_base *file, ui32 num_comps)
bool read(infile_base *file, bool resilient)
point org
Definition ojph_base.h:66