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, "number of tiles cannot exceed 65535");
122
123 //allocate tiles
124 allocator->pre_alloc_obj<tile>((size_t)num_tiles.area());
125
126 ui32 num_tileparts = 0;
127 point index;
128 rect tile_rect, recon_tile_rect;
129 ui32 ds = 1 << skipped_res_for_recon;
130 for (index.y = 0; index.y < num_tiles.h; ++index.y)
131 {
132 ui32 y0 = sz.get_tile_offset().y
133 + index.y * sz.get_tile_size().h;
134 ui32 y1 = y0 + sz.get_tile_size().h; //end of tile
135
136 tile_rect.org.y = ojph_max(y0, sz.get_image_offset().y);
137 tile_rect.siz.h =
138 ojph_min(y1, sz.get_image_extent().y) - tile_rect.org.y;
139
140 recon_tile_rect.org.y = ojph_max(ojph_div_ceil(y0, ds),
142 recon_tile_rect.siz.h = ojph_min(ojph_div_ceil(y1, ds),
144 - recon_tile_rect.org.y;
145
146 for (index.x = 0; index.x < num_tiles.w; ++index.x)
147 {
148 ui32 x0 = sz.get_tile_offset().x
149 + index.x * sz.get_tile_size().w;
150 ui32 x1 = x0 + sz.get_tile_size().w;
151
152 tile_rect.org.x = ojph_max(x0, sz.get_image_offset().x);
153 tile_rect.siz.w =
154 ojph_min(x1, sz.get_image_extent().x) - tile_rect.org.x;
155
156 recon_tile_rect.org.x = ojph_max(ojph_div_ceil(x0, ds),
158 recon_tile_rect.siz.w = ojph_min(ojph_div_ceil(x1, ds),
160 - recon_tile_rect.org.x;
161
162 ui32 tps = 0; // number of tileparts for this tile
163 tile::pre_alloc(this, tile_rect, recon_tile_rect, tps);
164 num_tileparts += tps;
165 }
166 }
167
168 //allocate lines
169 //These lines are used by codestream to exchange data with external
170 // world
172 allocator->pre_alloc_obj<line_buf>(num_comps);
173 allocator->pre_alloc_obj<size>(num_comps); //for *comp_size
174 allocator->pre_alloc_obj<size>(num_comps); //for *recon_comp_size
175 for (ui32 i = 0; i < num_comps; ++i)
176 allocator->pre_alloc_data<si32>(siz.get_recon_width(i), 0);
177
178 //allocate tlm
179 if (outfile != NULL && need_tlm)
180 allocator->pre_alloc_obj<param_tlm::Ttlm_Ptlm_pair>(num_tileparts);
181
182 //precinct scratch buffer
183 ui32 num_decomps = cod.get_num_decompositions();
184 size log_cb = cod.get_log_block_dims();
185
186 size ratio;
187 for (ui32 r = 0; r <= num_decomps; ++r)
188 {
189 size log_PP = cod.get_log_precinct_size(r);
190 ratio.w = ojph_max(ratio.w, log_PP.w - ojph_min(log_cb.w, log_PP.w));
191 ratio.h = ojph_max(ratio.h, log_PP.h - ojph_min(log_cb.h, log_PP.h));
192 }
193 ui32 max_ratio = ojph_max(ratio.w, ratio.h);
194 max_ratio = 1 << max_ratio;
195 // assuming that we have a hierarchy of n levels.
196 // This needs 4/3 times the area, rounded up
197 // (rounding up leaves one extra entry).
198 // This exta entry is necessary
199 // We need 4 such tables. These tables store
200 // 1. missing msbs and 2. their flags,
201 // 3. number of layers and 4. their flags
203 4 * ((max_ratio * max_ratio * 4 + 2) / 3);
204
206 }
207
210 {
211 allocator->alloc();
212
213 //precinct scratch buffer
216
217 //get tiles
218 tiles = this->allocator->post_alloc_obj<tile>((size_t)num_tiles.area());
219
220 ui32 num_tileparts = 0;
221 point index;
222 rect tile_rect;
224 for (index.y = 0; index.y < num_tiles.h; ++index.y)
225 {
226 ui32 y0 = sz.get_tile_offset().y
227 + index.y * sz.get_tile_size().h;
228 ui32 y1 = y0 + sz.get_tile_size().h; //end of tile
229
230 tile_rect.org.y = ojph_max(y0, sz.get_image_offset().y);
231 tile_rect.siz.h =
232 ojph_min(y1, sz.get_image_extent().y) - tile_rect.org.y;
233
234 ui32 offset = 0;
235 for (index.x = 0; index.x < num_tiles.w; ++index.x)
236 {
237 ui32 x0 = sz.get_tile_offset().x
238 + index.x * sz.get_tile_size().w;
239 ui32 x1 = x0 + sz.get_tile_size().w;
240
241 tile_rect.org.x = ojph_max(x0, sz.get_image_offset().x);
242 tile_rect.siz.w =
243 ojph_min(x1, sz.get_image_extent().x) - tile_rect.org.x;
244
245 ui32 tps = 0; // number of tileparts for this tile
246 ui32 idx = index.y * num_tiles.w + index.x;
247 tiles[idx].finalize_alloc(this, tile_rect, idx, offset, tps);
248 num_tileparts += tps;
249 }
250 }
251
252 //allocate lines
253 //These lines are used by codestream to exchange data with external
254 // world
255 this->num_comps = sz.get_num_components();
256 lines = allocator->post_alloc_obj<line_buf>(this->num_comps);
257 comp_size = allocator->post_alloc_obj<size>(this->num_comps);
258 recon_comp_size = allocator->post_alloc_obj<size>(this->num_comps);
259 employ_color_transform = cod.is_employing_color_transform();
260 for (ui32 i = 0; i < this->num_comps; ++i)
261 {
262 comp_size[i].w = siz.get_width(i);
263 comp_size[i].h = siz.get_height(i);
264 ui32 cw = siz.get_recon_width(i);
265 recon_comp_size[i].w = cw;
266 recon_comp_size[i].h = siz.get_recon_height(i);
267 lines[i].wrap(allocator->post_alloc_data<si32>(cw, 0), cw, 0);
268 }
269
270 cur_comp = 0;
271 cur_line = 0;
272
273 //allocate tlm
274 if (outfile != NULL && need_tlm)
275 tlm.init(num_tileparts,
276 allocator->post_alloc_obj<param_tlm::Ttlm_Ptlm_pair>(num_tileparts));
277 }
278
279
282 {
283 //two possibilities lossy single tile or lossless
284 //For the following code, we use the least strict profile
285 ojph::param_siz sz(&siz);
286 ojph::param_cod cd(&cod);
287 bool reversible = cd.is_reversible();
288 bool imf2k = !reversible, imf4k = !reversible, imf8k = !reversible;
289 bool imf2kls = reversible, imf4kls = reversible, imf8kls = reversible;
290
291 if (reversible)
292 {
293 point ext = sz.get_image_extent();
294 if (ext.x <= 2048 && ext.y <= 1556)
295 imf2kls &= true;
296 if (ext.x <= 4096 && ext.y <= 3112)
297 imf4kls &= true;
298 if (ext.x <= 8192 && ext.y <= 6224)
299 imf8kls &= true;
300
301 if (!imf2kls && !imf4kls && !imf8kls)
302 OJPH_ERROR(0x000300C1,
303 "Image dimensions do not meet any of the lossless IMF profiles");
304 }
305 else
306 {
307 point ext = sz.get_image_extent();
308 if (ext.x <= 2048 && ext.y <= 1556)
309 imf2k &= true;
310 if (ext.x <= 4096 && ext.y <= 3112)
311 imf4k &= true;
312 if (ext.x <= 8192 && ext.y <= 6224)
313 imf8k &= true;
314
315 if (!imf2k && !imf4k && !imf8k)
316 OJPH_ERROR(0x000300C2,
317 "Image dimensions do not meet any of the lossy IMF profiles");
318 }
319
320
321 if (sz.get_image_offset().x != 0 || sz.get_image_offset().y != 0)
322 OJPH_ERROR(0x000300C3,
323 "For IMF profile, image offset (XOsiz, YOsiz) has to be 0.");
324 if (sz.get_tile_offset().x != 0 || sz.get_tile_offset().y != 0)
325 OJPH_ERROR(0x000300C4,
326 "For IMF profile, tile offset (XTOsiz, YTOsiz) has to be 0.");
327 if (sz.get_num_components() > 3)
328 OJPH_ERROR(0x000300C5,
329 "For IMF profile, the number of components has to be less "
330 " or equal to 3");
331 bool test_ds1 = true, test_ds2 = true;
332 for (ojph::ui32 i = 0; i < sz.get_num_components(); ++i)
333 {
334 point downsamping = sz.get_downsampling(i);
335 test_ds1 &= downsamping.y == 1;
336 test_ds2 &= downsamping.y == 1;
337
338 test_ds1 &= downsamping.x == 1;
339 if (i == 1 || i == 2)
340 test_ds2 &= downsamping.x == 2;
341 else
342 test_ds2 &= downsamping.x == 1;
343 }
344 if (!test_ds1 && !test_ds2)
345 OJPH_ERROR(0x000300C6,
346 "For IMF profile, either no component downsampling is used,"
347 " or the x-dimension of the 2nd and 3rd components is downsampled"
348 " by 2.");
349
350 bool test_bd = true;
351 for (ojph::ui32 i = 0; i < sz.get_num_components(); ++i)
352 {
353 ui32 bit_depth = sz.get_bit_depth(i);
354 bool is_signed = sz.is_signed(i);
355 test_bd &= bit_depth >= 8 && bit_depth <= 16 && is_signed == false;
356 }
357 if (!test_bd)
358 OJPH_ERROR(0x000300C7,
359 "For IMF profile, compnent bit_depth has to be between"
360 " 8 and 16 bits inclusively, and the samples must be unsigned");
361
362 if (cd.get_log_block_dims().w != 5 || cd.get_log_block_dims().h != 5)
363 OJPH_ERROR(0x000300C8,
364 "For IMF profile, codeblock dimensions are restricted."
365 " Use \"-block_size {32,32}\" at the commandline");
366
367 ui32 num_decomps = cd.get_num_decompositions();
368 bool test_pz = cd.get_log_precinct_size(0).w == 7
369 && cd.get_log_precinct_size(0).h == 7;
370 for (ui32 i = 1; i <= num_decomps; ++i)
371 test_pz = cd.get_log_precinct_size(i).w == 8
372 && cd.get_log_precinct_size(i).h == 8;
373 if (!test_pz)
374 OJPH_ERROR(0x000300C9,
375 "For IMF profile, precinct sizes are restricted."
376 " Use \"-precincts {128,128},{256,256}\" at the commandline");
377
379 OJPH_ERROR(0x000300CA,
380 "For IMF profile, the CPRL progression order must be used."
381 " Use \"-prog_order CPRL\".");
382
383 imf2k &= num_decomps <= 5;
384 imf2kls &= num_decomps <= 5;
385 imf4k &= num_decomps <= 6;
386 imf4kls &= num_decomps <= 6;
387 imf8k &= num_decomps <= 7;
388 imf8kls &= num_decomps <= 7;
389
390 if (num_decomps == 0 ||
391 (!imf2k && !imf4k && !imf8k && !imf2kls && !imf4kls && !imf8kls))
392 OJPH_ERROR(0x000300CB,
393 "Number of decompositions does not match the IMF profile"
394 " dictated by wavelet reversibility and image dimensions.");
395
396 ui32 tiles_w = sz.get_image_extent().x;
397 tiles_w = ojph_div_ceil(tiles_w, sz.get_tile_size().w);
398 ui32 tiles_h = sz.get_image_extent().y;
399 tiles_h = ojph_div_ceil(tiles_h, sz.get_tile_size().h);
400 ui32 total_tiles = tiles_w * tiles_h;
401
402 if (total_tiles > 1)
403 {
404 if (!reversible)
405 OJPH_ERROR(0x000300CC,
406 "Lossy IMF profile must have one tile.");
407
408 size tt = sz.get_tile_size();
409 imf2kls &= (tt.w == 1024 && tt.h == 1024);
410 imf2kls &= (tt.w >= 1024 && num_decomps <= 4)
411 || (tt.w >= 2048 && num_decomps <= 5);
412 imf4kls &= (tt.w == 1024 && tt.h == 1024)
413 || (tt.w == 2048 && tt.h == 2048);
414 imf4kls &= (tt.w >= 1024 && num_decomps <= 4)
415 || (tt.w >= 2048 && num_decomps <= 5)
416 || (tt.w >= 4096 && num_decomps <= 6);
417 imf8kls &= (tt.w == 1024 && tt.h == 1024)
418 || (tt.w == 2048 && tt.h == 2048)
419 || (tt.w == 4096 && tt.h == 4096);
420 imf8kls &= (tt.w >= 1024 && num_decomps <= 4)
421 || (tt.w >= 2048 && num_decomps <= 5)
422 || (tt.w >= 4096 && num_decomps <= 6)
423 || (tt.w >= 8192 && num_decomps <= 7);
424 if (!imf2kls && !imf4kls && !imf8kls)
425 OJPH_ERROR(0x000300CD,
426 "Number of decompositions does not match the IMF profile"
427 " dictated by wavelet reversibility and image dimensions and"
428 " tiles.");
429 }
430
431 need_tlm = true;
434 {
436 OJPH_WARN(0x000300C1,
437 "In IMF profile, tile part divisions at the component level must be "
438 "employed, while at the resolution level is not allowed. "
439 "This has been corrected.");
440 }
441 }
442
445 {
446 ojph::param_siz sz(&siz);
447 ojph::param_cod cd(&cod);
448
449 if (sz.get_image_offset().x != 0 || sz.get_image_offset().y != 0)
450 OJPH_ERROR(0x000300B1,
451 "For broadcast profile, image offset (XOsiz, YOsiz) has to be 0.");
452 if (sz.get_tile_offset().x != 0 || sz.get_tile_offset().y != 0)
453 OJPH_ERROR(0x000300B2,
454 "For broadcast profile, tile offset (XTOsiz, YTOsiz) has to be 0.");
455 if (sz.get_num_components() > 4)
456 OJPH_ERROR(0x000300B3,
457 "For broadcast profile, the number of components has to be less "
458 " or equal to 4");
459 bool test_ds1 = true, test_ds2 = true;
460 for (ojph::ui32 i = 0; i < sz.get_num_components(); ++i)
461 {
462 point downsamping = sz.get_downsampling(i);
463 test_ds1 &= downsamping.y == 1;
464 test_ds2 &= downsamping.y == 1;
465
466 test_ds1 &= downsamping.x == 1;
467 if (i == 1 || i == 2)
468 test_ds2 &= downsamping.x == 2;
469 else
470 test_ds2 &= downsamping.x == 1;
471 }
472 if (!test_ds1 && !test_ds2)
473 OJPH_ERROR(0x000300B4,
474 "For broadcast profile, either no component downsampling is used,"
475 " or the x-dimension of the 2nd and 3rd components is downsampled"
476 " by 2.");
477
478 bool test_bd = true;
479 for (ojph::ui32 i = 0; i < sz.get_num_components(); ++i)
480 {
481 ui32 bit_depth = sz.get_bit_depth(i);
482 bool is_signed = sz.is_signed(i);
483 test_bd &= bit_depth >= 8 && bit_depth <= 12 && is_signed == false;
484 }
485 if (!test_bd)
486 OJPH_ERROR(0x000300B5,
487 "For broadcast profile, compnent bit_depth has to be between"
488 " 8 and 12 bits inclusively, and the samples must be unsigned");
489
490 ui32 num_decomps = cd.get_num_decompositions();
491 if (num_decomps == 0 || num_decomps > 5)
492 OJPH_ERROR(0x000300B6,
493 "For broadcast profile, number of decompositions has to be between"
494 "1 and 5 inclusively.");
495
496 if (cd.get_log_block_dims().w < 5 || cd.get_log_block_dims().w > 7)
497 OJPH_ERROR(0x000300B7,
498 "For broadcast profile, codeblock dimensions are restricted such"
499 " that codeblock width has to be either 32, 64, or 128.");
500
501 if (cd.get_log_block_dims().h < 5 || cd.get_log_block_dims().h > 7)
502 OJPH_ERROR(0x000300B8,
503 "For broadcast profile, codeblock dimensions are restricted such"
504 " that codeblock height has to be either 32, 64, or 128.");
505
506 bool test_pz = cd.get_log_precinct_size(0).w == 7
507 && cd.get_log_precinct_size(0).h == 7;
508 for (ui32 i = 1; i <= num_decomps; ++i)
509 test_pz = cd.get_log_precinct_size(i).w == 8
510 && cd.get_log_precinct_size(i).h == 8;
511 if (!test_pz)
512 OJPH_ERROR(0x000300B9,
513 "For broadcast profile, precinct sizes are restricted."
514 " Use \"-precincts {128,128},{256,256}\" at the commandline");
515
517 OJPH_ERROR(0x000300BA,
518 "For broadcast profile, the CPRL progression order must be used."
519 " Use \"-prog_order CPRL\".");
520
521 ui32 tiles_w = sz.get_image_extent().x;
522 tiles_w = ojph_div_ceil(tiles_w, sz.get_tile_size().w);
523 ui32 tiles_h = sz.get_image_extent().y;
524 tiles_h = ojph_div_ceil(tiles_h, sz.get_tile_size().h);
525 ui32 total_tiles = tiles_w * tiles_h;
526
527 if (total_tiles != 1 && total_tiles != 4)
528 OJPH_ERROR(0x000300BB,
529 "The broadcast profile can only have 1 or 4 tiles");
530
531 need_tlm = true;
534 {
536 OJPH_WARN(0x000300B1,
537 "In BROADCAST profile, tile part divisions at the component level "
538 "must be employed, while at the resolution level is not allowed. "
539 "This has been corrected.");
540 }
541 }
542
545 const comment_exchange* comments,
546 ui32 num_comments)
547 {
548 //finalize
549 siz.check_validity(cod);
550 cod.check_validity(siz);
551 cod.update_atk(&atk);
552 qcd.check_validity(siz, cod);
553 cap.check_validity(cod, qcd);
554 nlt.check_validity(siz);
555 if (profile == OJPH_PN_IMF)
557 else if (profile == OJPH_PN_BROADCAST)
559
561 if ((po == OJPH_PO_LRCP || po == OJPH_PO_RLCP) &&
563 {
565 OJPH_INFO(0x00030021,
566 "For LRCP and RLCP progression orders, tilepart divisions at the "
567 "component level, means that we have a tilepart for every "
568 "resolution and component.\n");
569 }
571 {
573 OJPH_WARN(0x00030021,
574 "For RPCL progression, having tilepart divisions at the component "
575 "level means a tilepart for every precinct, which does not "
576 "make sense, since we can have no more than 255 tile parts. This "
577 "has been corrected by removing tilepart divisions at the component "
578 "level.");
579 }
580 if (po == OJPH_PO_PCRL && tilepart_div != 0)
581 {
582 tilepart_div = 0;
583 OJPH_WARN(0x00030022,
584 "For PCRL progression, having tilepart divisions at the component "
585 "level or the resolution level means a tile part for every "
586 "precinct, which does not make sense, since we can have no more "
587 "than 255 tile parts. This has been corrected by removing tilepart "
588 "divisions; use another progression if you want tileparts.");
589 }
591 {
593 OJPH_WARN(0x00030023,
594 "For CPRL progression, having tilepart divisions at the resolution "
595 "level means a tile part for every precinct, which does not "
596 "make sense, since we can have no more than 255 tile parts. This "
597 "has been corrected by removing tilepart divisions at the "
598 "resolution level.");
599 }
600
601 if (planar == -1) //not initialized
602 planar = cod.is_employing_color_transform() ? 1 : 0;
603 else if (planar == 0) //interleaved is chosen
604 {
605 }
606 else if (planar == 1) //plannar is chosen
607 {
608 if (cod.is_employing_color_transform() == true)
609 OJPH_ERROR(0x00030021,
610 "the planar interface option cannot be used when colour "
611 "transform is employed");
612 }
613 else
614 assert(0);
615
616 assert(this->outfile == NULL);
617 this->outfile = file;
618 this->pre_alloc();
619 this->finalize_alloc();
620
622 if (file->write(&t, 2) != 2)
623 OJPH_ERROR(0x00030022, "Error writing to file");
624
625 if (!siz.write(file))
626 OJPH_ERROR(0x00030023, "Error writing to file");
627
628 if (!cap.write(file))
629 OJPH_ERROR(0x00030024, "Error writing to file");
630
631 if (!cod.write(file))
632 OJPH_ERROR(0x00030025, "Error writing to file");
633
634 if (!cod.write_coc(file, num_comps))
635 OJPH_ERROR(0x0003002E, "Error writing to file");
636
637 if (!qcd.write(file))
638 OJPH_ERROR(0x00030026, "Error writing to file");
639
640 if (!qcd.write_qcc(file, num_comps))
641 OJPH_ERROR(0x0003002D, "Error writing to file");
642
643 if (!nlt.write(file))
644 OJPH_ERROR(0x00030027, "Error writing to file");
645
646 char buf[] = " OpenJPH Ver "
650 size_t len = strlen(buf);
652 *(ui16*)(buf + 2) = swap_byte((ui16)(len - 2));
653 //1 for General use (IS 8859-15:1999 (Latin) values)
654 *(ui16*)(buf + 4) = swap_byte((ui16)(1));
655 if (file->write(buf, len) != len)
656 OJPH_ERROR(0x00030028, "Error writing to file");
657
658 if (comments != NULL) {
659 for (ui32 i = 0; i < num_comments; ++i)
660 {
662 if (file->write(&t, 2) != 2)
663 OJPH_ERROR(0x00030029, "Error writing to file");
664 t = swap_byte((ui16)(comments[i].len + 4));
665 if (file->write(&t, 2) != 2)
666 OJPH_ERROR(0x0003002A, "Error writing to file");
667 //1 for General use (IS 8859-15:1999 (Latin) values)
668 t = swap_byte(comments[i].Rcom);
669 if (file->write(&t, 2) != 2)
670 OJPH_ERROR(0x0003002B, "Error writing to file");
671 if (file->write(comments[i].data, comments[i].len)!=comments[i].len)
672 OJPH_ERROR(0x0003002C, "Error writing to file");
673 }
674 }
675 }
676
678 static
679 int find_marker(infile_base *f, const ui16* char_list, int list_len)
680 {
681 //returns the marker index in char_list, or -1
682 while (!f->eof())
683 {
684 ui8 new_char;
685 size_t num_bytes = f->read(&new_char, 1);
686 if (num_bytes != 1)
687 return -1;
688 if (new_char == 0xFF)
689 {
690 size_t num_bytes = f->read(&new_char, 1);
691
692 if (num_bytes != 1)
693 return -1;
694
695 for (int i = 0; i < list_len; ++i)
696 if (new_char == (char_list[i] & 0xFF))
697 return i;
698 }
699 }
700 return -1;
701 }
702
704 static
705 int skip_marker(infile_base *file, const char *marker,
706 const char *msg, int msg_level, bool resilient)
707 {
708 ojph_unused(marker);
709 ui16 com_len;
710 if (file->read(&com_len, 2) != 2)
711 {
712 if (resilient)
713 return -1;
714 else
715 OJPH_ERROR(0x00030041, "error reading marker");
716 }
717 com_len = swap_byte(com_len);
718 file->seek(com_len - 2, infile_base::OJPH_SEEK_CUR);
719 if (msg != NULL && msg_level != OJPH_MSG_LEVEL::NO_MSG)
720 {
721 if (msg_level == OJPH_MSG_LEVEL::INFO)
722 {
723 OJPH_INFO(0x00030001, "%s", msg);
724 }
725 else if (msg_level == OJPH_MSG_LEVEL::WARN)
726 {
727 OJPH_WARN(0x00030001, "%s", msg);
728 }
729 else if (msg_level == OJPH_MSG_LEVEL::ERROR)
730 {
731 OJPH_ERROR(0x00030001, "%s", msg);
732 }
733 else // there is the option of ALL_MSG but it should not be used here
734 assert(0);
735 }
736 return 0;
737 }
738
741 {
742 ui16 marker_list[20] = { SOC, SIZ, CAP, PRF, CPF, COD, COC, QCD, QCC,
743 RGN, POC, PPM, TLM, PLM, CRG, COM, DFS, ATK, NLT, SOT };
744 find_marker(file, marker_list, 1); //find SOC
745 find_marker(file, marker_list + 1, 1); //find SIZ
746 siz.read(file);
747 int marker_idx = 0;
748 int received_markers = 0; //check that COD, & QCD received
749 while (true)
750 {
751 marker_idx = find_marker(file, marker_list + 2, 18);
752 if (marker_idx == 0)
753 cap.read(file);
754 else if (marker_idx == 1)
755 //Skipping PRF marker segment; this should not cause any issues
756 skip_marker(file, "PRF", NULL, OJPH_MSG_LEVEL::NO_MSG, false);
757 else if (marker_idx == 2)
758 //Skipping CPF marker segment; this should not cause any issues
759 skip_marker(file, "CPF", NULL, OJPH_MSG_LEVEL::NO_MSG, false);
760 else if (marker_idx == 3)
761 {
762 cod.read(file);
763 received_markers |= 1;
765 int num_qlayers = c.get_num_layers();
766 if (num_qlayers != 1)
767 OJPH_ERROR(0x00030053, "The current implementation supports "
768 "1 quality layer only. This codestream has %d quality layers",
769 num_qlayers);
770 }
771 else if (marker_idx == 4)
772 {
773 param_cod* p = cod.add_coc_object(param_cod::OJPH_COD_UNKNOWN);
774 p->read_coc(file, siz.get_num_components(), &cod);
775 if (p->get_comp_idx() >= siz.get_num_components())
776 OJPH_INFO(0x00030056, "The codestream carries a COC marker "
777 "segment for a component indexed by %d, which is more than the "
778 "allowed index number, since the codestream has %d components",
779 p->get_comp_idx(), num_comps);
780 param_cod *q = cod.get_coc(p->get_comp_idx());
781 if (p != q && p->get_comp_idx() == q->get_comp_idx())
782 OJPH_ERROR(0x00030057, "The codestream has two COC marker "
783 "segments for one component of index %d", p->get_comp_idx());
784 }
785 else if (marker_idx == 5)
786 {
787 qcd.read(file);
788 received_markers |= 2;
789 }
790 else if (marker_idx == 6)
791 {
792 param_qcd* p = qcd.add_qcc_object(param_qcd::OJPH_QCD_UNKNOWN);
793 p->read_qcc(file, siz.get_num_components());
794 if (p->get_comp_idx() >= siz.get_num_components())
795 OJPH_ERROR(0x00030054, "The codestream carries a QCC marker "
796 "segment for a component indexed by %d, which is more than the "
797 "allowed index number, since the codestream has %d components",
798 p->get_comp_idx(), num_comps);
799 param_qcd *q = qcd.get_qcc(p->get_comp_idx());
800 if (p != q && p->get_comp_idx() == q->get_comp_idx())
801 OJPH_ERROR(0x00030055, "The codestream has two QCC marker "
802 "segments for one component of index %d", p->get_comp_idx());
803 }
804 else if (marker_idx == 7)
805 skip_marker(file, "RGN", "RGN is not supported yet",
806 OJPH_MSG_LEVEL::WARN, false);
807 else if (marker_idx == 8)
808 skip_marker(file, "POC", "POC is not supported yet",
809 OJPH_MSG_LEVEL::WARN, false);
810 else if (marker_idx == 9)
811 skip_marker(file, "PPM", "PPM is not supported yet",
812 OJPH_MSG_LEVEL::WARN, false);
813 else if (marker_idx == 10)
814 //Skipping TLM marker segment; this should not cause any issues
815 skip_marker(file, "TLM", NULL, OJPH_MSG_LEVEL::NO_MSG, false);
816 else if (marker_idx == 11)
817 //Skipping PLM marker segment; this should not cause any issues
818 skip_marker(file, "PLM", NULL, OJPH_MSG_LEVEL::NO_MSG, false);
819 else if (marker_idx == 12)
820 //Skipping CRG marker segment;
821 skip_marker(file, "CRG", "CRG has been ignored; CRG is related to"
822 " where the Cb and Cr colour components are co-sited or located"
823 " with respect to the Y' luma component. Perhaps, it is better"
824 " to get the individual components and assemble the samples"
825 " according to your needs",
826 OJPH_MSG_LEVEL::INFO, false);
827 else if (marker_idx == 13)
828 skip_marker(file, "COM", NULL, OJPH_MSG_LEVEL::NO_MSG, false);
829 else if (marker_idx == 14)
830 dfs.read(file);
831 else if (marker_idx == 15)
832 atk.read(file);
833 else if (marker_idx == 16)
834 nlt.read(file);
835 else if (marker_idx == 17)
836 break;
837 else
838 OJPH_ERROR(0x00030051, "File ended before finding a tile segment");
839 }
840
841 cod.update_atk(&atk);
842 siz.link(&cod);
843 if (dfs.exists())
844 siz.link(&dfs);
845
846 if (received_markers != 3)
847 OJPH_ERROR(0x00030052, "markers error, COD and QCD are required");
848
849 this->infile = file;
850 planar = cod.is_employing_color_transform() ? 0 : 1;
851 }
852
856 {
858 OJPH_ERROR(0x000300A1,
859 "skipped_resolution for data %d must be equal or smaller than "
860 " skipped_resolution for reconstruction %d\n",
862 if (skipped_res_for_read > cod.get_num_decompositions())
863 OJPH_ERROR(0x000300A2,
864 "skipped_resolution for data %d must be smaller than "
865 " the number of decomposition levels %d\n",
866 skipped_res_for_read, cod.get_num_decompositions());
867
868 this->skipped_res_for_read = skipped_res_for_read;
869 this->skipped_res_for_recon = skipped_res_for_recon;
870 siz.set_skipped_resolutions(skipped_res_for_recon);
871 }
872
875 {
876 if (infile != NULL)
877 OJPH_ERROR(0x000300A3, "Codestream resilience must be enabled before"
878 " reading file headers.\n");
879 this->resilient = true;
880 }
881
884 {
885 this->pre_alloc();
886 this->finalize_alloc();
887
888 while (true)
889 {
890 param_sot sot;
891 if (sot.read(infile, resilient))
892 {
893 ui64 tile_start_location = (ui64)infile->tell();
894
895 if (sot.get_tile_index() > (int)num_tiles.area())
896 {
897 if (resilient)
898 OJPH_INFO(0x00030061, "wrong tile index")
899 else
900 OJPH_ERROR(0x00030061, "wrong tile index")
901 }
902
903 if (sot.get_tile_part_index())
904 { //tile part
905 if (sot.get_num_tile_parts() &&
907 {
908 if (resilient)
909 OJPH_INFO(0x00030062,
910 "error in tile part number, should be smaller than total"
911 " number of tile parts")
912 else
913 OJPH_ERROR(0x00030062,
914 "error in tile part number, should be smaller than total"
915 " number of tile parts")
916 }
917
918 bool sod_found = false;
919 ui16 other_tile_part_markers[7] = { SOT, POC, PPT, PLT, COM,
920 NLT, SOD };
921 while (true)
922 {
923 int marker_idx = 0;
924 int result = 0;
925 marker_idx = find_marker(infile, other_tile_part_markers + 1, 6);
926 if (marker_idx == 0)
927 result = skip_marker(infile, "POC",
928 "POC marker segment in a tile is not supported yet",
930 else if (marker_idx == 1)
931 result = skip_marker(infile, "PPT",
932 "PPT marker segment in a tile is not supported yet",
934 else if (marker_idx == 2)
935 //Skipping PLT marker segment;this should not cause any issues
936 result = skip_marker(infile, "PLT", NULL,
938 else if (marker_idx == 3)
939 result = skip_marker(infile, "COM", NULL,
941 else if (marker_idx == 4)
942 result = skip_marker(infile, "NLT",
943 "NLT marker in tile is not supported yet",
945 else if (marker_idx == 5)
946 {
947 sod_found = true;
948 break;
949 }
950
951 if (marker_idx == -1) //marker not found
952 {
953 if (resilient)
954 OJPH_INFO(0x00030063,
955 "File terminated early before start of data is found"
956 " for tile indexed %d and tile part %d",
958 else
959 OJPH_ERROR(0x00030063,
960 "File terminated early before start of data is found"
961 " for tile indexed %d and tile part %d",
963 break;
964 }
965 if (result == -1) //file terminated during marker seg. skipping
966 {
967 if (resilient)
968 OJPH_INFO(0x00030064,
969 "File terminated during marker segment skipping")
970 else
971 OJPH_ERROR(0x00030064,
972 "File terminated during marker segment skipping")
973 break;
974 }
975 }
976 if (sod_found)
977 tiles[sot.get_tile_index()].parse_tile_header(sot, infile,
978 tile_start_location);
979 }
980 else
981 { //first tile part
982 bool sod_found = false;
983 ui16 first_tile_part_markers[12] = { SOT, COD, COC, QCD, QCC, RGN,
984 POC, PPT, PLT, COM, NLT, SOD };
985 while (true)
986 {
987 int marker_idx = 0;
988 int result = 0;
989 marker_idx = find_marker(infile, first_tile_part_markers+1, 11);
990 if (marker_idx == 0)
991 result = skip_marker(infile, "COD",
992 "COD marker segment in a tile is not supported yet",
994 else if (marker_idx == 1)
995 result = skip_marker(infile, "COC",
996 "COC marker segment in a tile is not supported yet",
998 else if (marker_idx == 2)
999 result = skip_marker(infile, "QCD",
1000 "QCD marker segment in a tile is not supported yet",
1002 else if (marker_idx == 3)
1003 result = skip_marker(infile, "QCC",
1004 "QCC marker segment in a tile is not supported yet",
1006 else if (marker_idx == 4)
1007 result = skip_marker(infile, "RGN",
1008 "RGN marker segment in a tile is not supported yet",
1010 else if (marker_idx == 5)
1011 result = skip_marker(infile, "POC",
1012 "POC marker segment in a tile is not supported yet",
1014 else if (marker_idx == 6)
1015 result = skip_marker(infile, "PPT",
1016 "PPT marker segment in a tile is not supported yet",
1018 else if (marker_idx == 7)
1019 //Skipping PLT marker segment;this should not cause any issues
1020 result = skip_marker(infile, "PLT", NULL,
1022 else if (marker_idx == 8)
1023 result = skip_marker(infile, "COM", NULL,
1025 else if (marker_idx == 9)
1026 result = skip_marker(infile, "NLT",
1027 "PPT marker segment in a tile is not supported yet",
1029 else if (marker_idx == 10)
1030 {
1031 sod_found = true;
1032 break;
1033 }
1034
1035 if (marker_idx == -1) //marker not found
1036 {
1037 if (resilient)
1038 OJPH_INFO(0x00030065,
1039 "File terminated early before start of data is found"
1040 " for tile indexed %d and tile part %d",
1042 else
1043 OJPH_ERROR(0x00030065,
1044 "File terminated early before start of data is found"
1045 " for tile indexed %d and tile part %d",
1047 break;
1048 }
1049 if (result == -1) //file terminated during marker seg. skipping
1050 {
1051 if (resilient)
1052 OJPH_INFO(0x00030066,
1053 "File terminated during marker segment skipping")
1054 else
1055 OJPH_ERROR(0x00030066,
1056 "File terminated during marker segment skipping")
1057 break;
1058 }
1059 }
1060 if (sod_found)
1061 tiles[sot.get_tile_index()].parse_tile_header(sot, infile,
1062 tile_start_location);
1063 }
1064 }
1065
1066 // check the next marker; either SOT or EOC,
1067 // if something is broken, just an end of file
1068 ui16 next_markers[2] = { SOT, EOC };
1069 int marker_idx = find_marker(infile, next_markers, 2);
1070 if (marker_idx == -1)
1071 {
1072 OJPH_INFO(0x00030067, "File terminated early");
1073 break;
1074 }
1075 else if (marker_idx == 0)
1076 ;
1077 else if (marker_idx == 1)
1078 break;
1079 }
1080 }
1081
1084 {
1085 this->planar = planar;
1086 }
1087
1089 void codestream::set_profile(const char *s)
1090 {
1091 size_t len = strlen(s);
1092 if (len == 9 && strncmp(s, OJPH_PN_STRING_BROADCAST, 9) == 0)
1094 else if (len == 3 && strncmp(s, OJPH_PN_STRING_IMF, 3) == 0)
1096 else
1097 OJPH_ERROR(0x000300A1, "unkownn or unsupported profile");
1098 }
1099
1102 {
1103 tilepart_div = value;
1104 }
1105
1108 {
1109 need_tlm = needed;
1110 }
1111
1114 {
1115 si32 repeat = (si32)num_tiles.area();
1116 for (si32 i = 0; i < repeat; ++i)
1117 tiles[i].prepare_for_flush();
1118 if (need_tlm)
1119 { //write tlm
1120 for (si32 i = 0; i < repeat; ++i)
1121 tiles[i].fill_tlm(&tlm);
1122 tlm.write(outfile);
1123 }
1124 for (si32 i = 0; i < repeat; ++i)
1125 tiles[i].flush(outfile);
1127 if (!outfile->write(&t, 2))
1128 OJPH_ERROR(0x00030071, "Error writing to file");
1129 }
1130
1133 {
1134 if (infile)
1135 infile->close();
1136 if (outfile)
1137 outfile->close();
1138 }
1139
1142 {
1143 if (line)
1144 {
1145 bool success = false;
1146 while (!success)
1147 {
1148 success = true;
1149 for (ui32 i = 0; i < num_tiles.w; ++i)
1150 {
1151 ui32 idx = i + cur_tile_row * num_tiles.w;
1152 if ((success &= tiles[idx].push(line, cur_comp)) == false)
1153 break;
1154 }
1155 cur_tile_row += success == false ? 1 : 0;
1156 if (cur_tile_row >= num_tiles.h)
1157 cur_tile_row = 0;
1158 }
1159
1160 if (planar) //process one component at a time
1161 {
1162 if (++cur_line >= comp_size[cur_comp].h)
1163 {
1164 cur_line = 0;
1165 cur_tile_row = 0;
1166 if (++cur_comp >= num_comps)
1167 {
1168 next_component = 0;
1169 return NULL;
1170 }
1171 }
1172 }
1173 else //process all component for a line
1174 {
1175 if (++cur_comp >= num_comps)
1176 {
1177 cur_comp = 0;
1178 if (++cur_line >= comp_size[cur_comp].h)
1179 {
1180 next_component = 0;
1181 return NULL;
1182 }
1183 }
1184 }
1185 }
1186
1187 next_component = cur_comp;
1188 return this->lines + cur_comp;
1189 }
1190
1193 {
1194 bool success = false;
1195 while (!success)
1196 {
1197 success = true;
1198 for (ui32 i = 0; i < num_tiles.w; ++i)
1199 {
1200 ui32 idx = i + cur_tile_row * num_tiles.w;
1201 if ((success &= tiles[idx].pull(lines + cur_comp, cur_comp)) == false)
1202 break;
1203 }
1204 cur_tile_row += success == false ? 1 : 0;
1205 if (cur_tile_row >= num_tiles.h)
1206 cur_tile_row = 0;
1207 }
1208 comp_num = cur_comp;
1209
1210 if (planar) //process one component at a time
1211 {
1212 if (++cur_line >= recon_comp_size[cur_comp].h)
1213 {
1214 cur_line = 0;
1215 cur_tile_row = 0;
1216 if (cur_comp++ >= num_comps)
1217 {
1218 comp_num = 0;
1219 return NULL;
1220 }
1221 }
1222 }
1223 else //process all component for a line
1224 {
1225 if (++cur_comp >= num_comps)
1226 {
1227 cur_comp = 0;
1228 if (cur_line++ >= recon_comp_size[cur_comp].h)
1229 {
1230 comp_num = 0;
1231 return NULL;
1232 }
1233 }
1234 }
1235
1236 return lines + comp_num;
1237 }
1238
1239 }
1240}
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
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