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_params_local.h
34 // Author: Aous Naman
35 // Date: 28 August 2019
36 //***************************************************************************/
37 
38 
39 #ifndef OJPH_PARAMS_LOCAL_H
40 #define OJPH_PARAMS_LOCAL_H
41 
42 #include <cstring>
43 #include <cassert>
44 
45 #include "ojph_defs.h"
46 #include "ojph_arch.h"
47 #include "ojph_message.h"
48 
49 namespace ojph {
50 
51   ////////////////////////////////////////////////////////////////////////////
52   class outfile_base;
53   class infile_base;
54 
55   ////////////////////////////////////////////////////////////////////////////
56   enum PROGRESSION_ORDER : si32
57   {
58     OJPH_PO_LRCP = 0,
59     OJPH_PO_RLCP = 1,
60     OJPH_PO_RPCL = 2,
61     OJPH_PO_PCRL = 3,
62     OJPH_PO_CPRL = 4
63   };
64 
65   ////////////////////////////////////////////////////////////////////////////
66   const char OJPH_PO_STRING_LRCP[] = "LRCP";
67   const char OJPH_PO_STRING_RLCP[] = "RLCP";
68   const char OJPH_PO_STRING_RPCL[] = "RPCL";
69   const char OJPH_PO_STRING_PCRL[] = "PCRL";
70   const char OJPH_PO_STRING_CPRL[] = "CPRL";
71 
72   ////////////////////////////////////////////////////////////////////////////
73   enum OJPH_PROFILE_NUM : si32
74   {
75     OJPH_PN_UNDEFINED = 0,
76     OJPH_PN_PROFILE0 = 1,
77     OJPH_PN_PROFILE1 = 2,
78     OJPH_PN_CINEMA2K = 3,
79     OJPH_PN_CINEMA4K = 4,
80     OJPH_PN_CINEMAS2K = 5,
81     OJPH_PN_CINEMAS4K = 6,
82     OJPH_PN_BROADCAST = 7,
83     OJPH_PN_IMF = 8
84   };
85 
86   ////////////////////////////////////////////////////////////////////////////
87   const char OJPH_PN_STRING_PROFILE0[] = "PROFILE0";
88   const char OJPH_PN_STRING_PROFILE1[] = "PROFILE1";
89   const char OJPH_PN_STRING_CINEMA2K[] = "CINEMA2K";
90   const char OJPH_PN_STRING_CINEMA4K[] = "CINEMA4K";
91   const char OJPH_PN_STRING_CINEMAS2K[] = "CINEMAS2K";
92   const char OJPH_PN_STRING_CINEMAS4K[] = "CINEMAS4K";
93   const char OJPH_PN_STRING_BROADCAST[] = "BROADCAST";
94   const char OJPH_PN_STRING_IMF[] = "IMF";
95 
96   namespace local {
97 
98     //////////////////////////////////////////////////////////////////////////
99     enum JP2K_MARKER : ui16
100     {
101       SOC = 0xFF4F, //start of codestream (required)
102       CAP = 0xFF50, //extended capability
103       SIZ = 0xFF51, //image and tile size (required)
104       COD = 0xFF52, //coding style default (required)
105       TLM = 0xFF55, //tile-part lengths
106       PRF = 0xFF56, //profile
107       PLM = 0xFF57, //packet length, main header
108       PLT = 0xFF58, //packet length, tile-part header
109       CPF = 0xFF59, //corresponding profile values
110       QCD = 0xFF5C, //qunatization default (required)
111       QCC = 0xFF5D, //quantization component
112       COM = 0xFF64, //comment
113       SOT = 0xFF90, //start of tile-part
114       SOP = 0xFF91, //start of packet
115       EPH = 0xFF92, //end of packet
116       SOD = 0xFF93, //start of data
117       EOC = 0xFFD9, //end of codestream (required)
118 
119       COC = 0xFF53, //coding style component
120       RGN = 0xFF5E, //region of interest
121       POC = 0xFF5F, //progression order change
122       PPM = 0xFF60, //packed packet headers, main header
123       PPT = 0xFF61, //packed packet headers, tile-part header
124       CRG = 0xFF63, //component registration
125     };
126 
127     //////////////////////////////////////////////////////////////////////////
128     //
129     //
130     //
131     //
132     //
133     //////////////////////////////////////////////////////////////////////////
134     struct siz_comp_info
135     {
136       ui8 SSiz;
137       ui8 XRsiz;
138       ui8 YRsiz;
139     };
140 
141     //////////////////////////////////////////////////////////////////////////
142     struct param_siz
143     {
144       friend ::ojph::param_siz;
145 
146     public:
param_sizparam_siz147       param_siz()
148       {
149         memset(this, 0, sizeof(param_siz));
150         cptr = store;
151         old_Csiz = 4;
152         Rsiz = 0x4000; //for jph, bit 14 of Rsiz is 1
153       }
154 
~param_sizparam_siz155       ~param_siz()
156       {
157         if (cptr != store) delete[] cptr;
158       }
159 
set_num_componentsparam_siz160       void set_num_components(int num_comps)
161       {
162         Csiz = (ui16)num_comps;
163         if (Csiz > old_Csiz)
164         {
165           if (cptr != store)
166             delete[] cptr;
167           cptr = new siz_comp_info[num_comps];
168           old_Csiz = Csiz;
169         }
170         memset(cptr, 0, sizeof(local::siz_comp_info) * num_comps);
171       }
172 
set_comp_infoparam_siz173       void set_comp_info(si32 comp_num, const point& downsampling,
174                          si32 bit_depth, bool is_signed)
175       {
176         assert(comp_num < Csiz);
177         assert(downsampling.x != 0 && downsampling.y != 0);
178         cptr[comp_num].SSiz = (ui8)(bit_depth - 1 + (is_signed ? 0x80 : 0));
179         cptr[comp_num].XRsiz = (ui8)downsampling.x;
180         cptr[comp_num].YRsiz = (ui8)downsampling.y;
181       }
182 
check_validityparam_siz183       void check_validity()
184       {
185         if (XTsiz == 0 && YTsiz == 0)
186         { XTsiz = Xsiz - XOsiz; YTsiz = Ysiz - YOsiz; }
187         if (Xsiz == 0 || Ysiz == 0 || XTsiz == 0 || YTsiz == 0)
188           OJPH_ERROR(0x00040001,
189             "You cannot set image extent nor tile size to zero");
190         if (XTOsiz > XOsiz || YTOsiz > YOsiz)
191           OJPH_ERROR(0x00040002,
192             "tile offset has to be smaller than image offset");
193         if (XTsiz + XTOsiz <= XOsiz || YTsiz + YTOsiz <= YOsiz)
194           OJPH_ERROR(0x00040003,
195             "the top left tile must intersect with the image");
196       }
197 
get_num_componentsparam_siz198       ui16 get_num_components() const { return Csiz; }
get_bit_depthparam_siz199       si32 get_bit_depth(si32 comp_num) const
200       {
201         assert(comp_num < Csiz);
202         return (cptr[comp_num].SSiz & 0x7F) + 1;
203       }
is_signedparam_siz204       bool is_signed(si32 comp_num) const
205       {
206         assert(comp_num < Csiz);
207         return (cptr[comp_num].SSiz & 0x80) != 0;
208       }
get_downsamplingparam_siz209       point get_downsampling(si32 comp_num) const
210       {
211         assert(comp_num < Csiz);
212         return point(cptr[comp_num].XRsiz, cptr[comp_num].YRsiz);
213       }
214 
215       bool write(outfile_base *file);
216       void read(infile_base *file);
217 
set_skipped_resolutionsparam_siz218       void set_skipped_resolutions(int skipped_resolutions)
219       {
220         this->skipped_resolutions = skipped_resolutions;
221       }
get_widthparam_siz222       ui32 get_width(int comp_num) const
223       {
224         assert(comp_num < get_num_components());
225         ui32 ds = (ui32)cptr[comp_num].XRsiz;
226         ui32 t = ojph_div_ceil(Xsiz, ds) - ojph_div_ceil(XOsiz, ds);
227         return t;
228       }
get_heightparam_siz229       ui32 get_height(int comp_num) const
230       {
231         assert(comp_num < get_num_components());
232         ui32 ds = (ui32)cptr[comp_num].YRsiz;
233         ui32 t = ojph_div_ceil(Ysiz, ds) - ojph_div_ceil(YOsiz, ds);
234         return t;
235       }
get_recon_widthparam_siz236       ui32 get_recon_width(int comp_num) const
237       {
238         assert(comp_num < get_num_components());
239         ui32 ds = (ui32)cptr[comp_num].XRsiz * (1u << skipped_resolutions);
240         ui32 t = ojph_div_ceil(Xsiz, ds) - ojph_div_ceil(XOsiz, ds);
241         return t;
242       }
get_recon_heightparam_siz243       ui32 get_recon_height(int comp_num) const
244       {
245         assert(comp_num < get_num_components());
246         ui32 ds = (ui32)cptr[comp_num].YRsiz * (1u << skipped_resolutions);
247         ui32 t = ojph_div_ceil(Ysiz, ds) - ojph_div_ceil(YOsiz, ds);
248         return t;
249       }
250 
251     private:
252       ui16 Lsiz;
253       ui16 Rsiz;
254       ui32 Xsiz;
255       ui32 Ysiz;
256       ui32 XOsiz;
257       ui32 YOsiz;
258       ui32 XTsiz;
259       ui32 YTsiz;
260       ui32 XTOsiz;
261       ui32 YTOsiz;
262       ui16 Csiz;
263       siz_comp_info* cptr;
264 
265     private:
266       int skipped_resolutions;
267       int old_Csiz;
268       siz_comp_info store[4];
269       param_siz(const param_siz&) = delete; //prevent copy constructor
270       param_siz& operator=(const param_siz&) = delete; //prevent copy
271     };
272 
273     ///////////////////////////////////////////////////////////////////////////
274     //
275     //
276     //
277     //
278     //
279     ///////////////////////////////////////////////////////////////////////////
280     struct cod_SPcod
281     {
282       ui8 num_decomp;
283       ui8 block_width;
284       ui8 block_height;
285       ui8 block_style;
286       ui8 wavelet_trans;
287       ui8 precinct_size[33]; //num_decomp is in [0,32]
288     };
289 
290     ///////////////////////////////////////////////////////////////////////////
291     typedef cod_SPcod cod_SPcoc;
292 
293     ///////////////////////////////////////////////////////////////////////////
294     struct cod_SGcod
295     {
296       ui8 prog_order;
297       ui16 num_layers;
298       ui8 mc_trans;
299     };
300 
301     ///////////////////////////////////////////////////////////////////////////
302     struct param_cod
303     {
304       friend ::ojph::param_cod;
305     public:
param_codparam_cod306       param_cod()
307       {
308         memset(this, 0, sizeof(param_cod));
309         SPcod.block_style = 0x40;
310         SGCod.prog_order = 2;
311         SGCod.num_layers = 1;
312         SGCod.mc_trans = 0;
313         SPcod.num_decomp = 5;
314         SPcod.block_width = 4; //64
315         SPcod.block_height = 4; //64
316         set_reversible(false);
317       }
318 
set_reversibleparam_cod319       void set_reversible(bool reversible)
320       {
321         SPcod.wavelet_trans = reversible ? 1 : 0;
322       }
323 
employ_color_transformparam_cod324       void employ_color_transform(ui8 val)
325       {
326         assert(val == 0 || val == 1);
327         SGCod.mc_trans = val;
328       }
329 
check_validityparam_cod330       void check_validity(const param_siz& siz)
331       {
332         //check that colour transform and match number of components and
333         // downsampling
334         int num_comps = siz.get_num_components();
335         if (SGCod.mc_trans == 1 && num_comps < 3)
336           OJPH_ERROR(0x00040011,
337             "color transform can only be employed when the image has 3 or "
338             "more color components");
339 
340         if (SGCod.mc_trans == 1)
341         {
342           bool test = false;
343           point p = siz.get_downsampling(0);
344           for (int i = 1; i < 3; ++i)
345           {
346             point p1 = siz.get_downsampling(i);
347             test = test || (p.x != p1.x || p.y != p1.y);
348           }
349           if (test)
350             OJPH_ERROR(0x00040012,
351               "when color transform is used, the first 3 colour "
352               "components must have the same downsampling.");
353         }
354 
355         //check the progression order matches downsampling
356         if (SGCod.prog_order == 2 || SGCod.prog_order == 3)
357         {
358           int num_comps = siz.get_num_components();
359           for (int i = 0; i < num_comps; ++i)
360           {
361             point r = siz.get_downsampling(i);
362             if (r.x & (r.x - 1) || r.y & (r.y - 1))
363               OJPH_ERROR(0x00040013, "For RPCL and PCRL progression orders,"
364                 "component downsampling factors have to be powers of 2");
365           }
366         }
367       }
368 
get_num_decompositionsparam_cod369       ui8 get_num_decompositions() const
370       { return SPcod.num_decomp; }
get_block_dimsparam_cod371       size get_block_dims() const
372       {
373         return size(1 << (SPcod.block_width + 2),
374                     1 << (SPcod.block_height + 2));
375       }
is_reversibleparam_cod376       bool is_reversible() const
377       { return (SPcod.wavelet_trans == 1); }
is_employing_color_transformparam_cod378       bool is_employing_color_transform() const
379       { return (SGCod.mc_trans == 1); }
get_log_block_dimsparam_cod380       size get_log_block_dims() const
381       { return size(SPcod.block_width + 2, SPcod.block_height + 2); }
get_precinct_sizeparam_cod382       size get_precinct_size(int res_num) const
383       {
384         size t = get_log_precinct_size(res_num);
385         t.w = 1 << t.w;
386         t.h = 1 << t.h;
387         return t;
388       }
get_log_precinct_sizeparam_cod389       size get_log_precinct_size(int res_num) const
390       {
391         assert(res_num <= SPcod.num_decomp);
392         size ps(15, 15);
393         if (Scod & 1)
394         {
395           ps.w = SPcod.precinct_size[res_num] & 0xF;
396           ps.h = SPcod.precinct_size[res_num] >> 4;
397         }
398         return ps;
399       }
packets_may_use_sopparam_cod400       bool packets_may_use_sop() const
401       { return (Scod & 2) == 2; }
packets_use_ephparam_cod402       bool packets_use_eph() const
403       { return (Scod & 4) == 4; }
404 
405       bool write(outfile_base *file);
406       void read(infile_base *file);
407 
408     private:
409       ui16 Lcod;
410       ui8 Scod;
411       cod_SGcod SGCod;
412       cod_SPcod SPcod;
413     };
414 
415     ///////////////////////////////////////////////////////////////////////////
416     //
417     //
418     //
419     //
420     //
421     ///////////////////////////////////////////////////////////////////////////
422     struct param_qcd
423     {
424       friend ::ojph::param_qcd;
425     public:
param_qcdparam_qcd426       param_qcd()
427       {
428         Lqcd = 0;
429         Sqcd = 0;
430         for (int i = 0; i < 97; ++i)
431           u16_SPqcd[i] = 0;
432         num_decomps = 0;
433         base_delta = -1.0f;
434       }
435 
set_deltaparam_qcd436       void set_delta(float delta) { base_delta = delta; }
437       void set_rev_quant(int bit_depth, bool is_employing_color_transform);
438       void set_irrev_quant();
439 
check_validityparam_qcd440       void check_validity(const param_siz& siz, const param_cod& cod)
441       {
442         num_decomps = cod.get_num_decompositions();
443         if (cod.is_reversible())
444         {
445           int bit_depth = 0;
446           for (int i = 0; i < siz.get_num_components(); ++i)
447             bit_depth = ojph_max(bit_depth, siz.get_bit_depth(i));
448           set_rev_quant(bit_depth, cod.is_employing_color_transform());
449         }
450         else
451         {
452           if (base_delta == -1.0f)
453             base_delta = 1.0f /
454               (float)(1 << (siz.get_bit_depth(0) + siz.is_signed(0)));
455           set_irrev_quant();
456          }
457       }
458 
459       int get_num_guard_bits() const;
460       int get_MAGBp() const;
461       int get_Kmax(int resolution, int subband) const;
462       float irrev_get_delta(int resolution, int subband) const;
463 
464       bool write(outfile_base *file);
465       void read(infile_base *file);
466 
467     protected:
468       ui16 Lqcd;
469       ui8 Sqcd;
470       union
471       {
472         ui8 u8_SPqcd[97];
473         ui16 u16_SPqcd[97];
474       };
475       int num_decomps;
476       float base_delta;
477     };
478 
479     ///////////////////////////////////////////////////////////////////////////
480     //
481     //
482     //
483     //
484     //
485     ///////////////////////////////////////////////////////////////////////////
486     struct param_qcc : public param_qcd
487     {
488       friend ::ojph::param_qcc;
489     public:
param_qccparam_qcc490       param_qcc() : param_qcd()
491       { comp_idx = 0; }
492 
get_comp_numparam_qcc493       ui16 get_comp_num() { return comp_idx; }
494       void read(infile_base *file, int num_comps);
495 
496     protected:
497         ui16 comp_idx;
498     };
499 
500     ///////////////////////////////////////////////////////////////////////////
501     //
502     //
503     //
504     //
505     //
506     ///////////////////////////////////////////////////////////////////////////
507     struct param_cap
508     {
509     public:
param_capparam_cap510       param_cap()
511       {
512         memset(this, 0, sizeof(param_cap));
513         Lcap = 8;
514         Pcap = 0x00020000; //for jph, Pcap^15 must be set, the 15th MSB
515       }
516 
check_validityparam_cap517       void check_validity(const param_cod& cod, const param_qcd& qcd)
518       {
519         if (cod.is_reversible())
520           Ccap[0] &= 0xFFDF;
521         else
522           Ccap[0] |= 0x0020;
523         Ccap[0] &= 0xFFE0;
524         int Bp = 0;
525         int B = qcd.get_MAGBp();
526         if (B <= 8)
527           Bp = 0;
528         else if (B < 28)
529           Bp = B - 8;
530         else if (B < 48)
531           Bp = 13 + (B >> 2);
532         else
533           Bp = 31;
534         Ccap[0] |= (ui16)Bp;
535       }
536 
537       bool write(outfile_base *file);
538       void read(infile_base *file);
539 
540     private:
541       ui16 Lcap;
542       ui32 Pcap;
543       ui16 Ccap[32]; //a maximum of 32
544     };
545 
546 
547     ///////////////////////////////////////////////////////////////////////////
548     //
549     //
550     //
551     //
552     //
553     ///////////////////////////////////////////////////////////////////////////
554     struct param_sot
555     {
556     public:
557       void init(ui32 payload_length = 0, ui16 tile_idx = 0,
558                 ui8 tile_part_index = 0, ui8 num_tile_parts = 0)
559       {
560         Lsot = 10;
561         Psot = payload_length + 12; //total = payload + SOT marker
562         Isot = tile_idx;
563         TPsot = tile_part_index;
564         TNsot = num_tile_parts;
565       }
566 
567       bool write(outfile_base *file, ui32 payload_len);
568       bool write(outfile_base *file, ui32 payload_len, ui8 TPsot, ui8 TNsot);
569       bool read(infile_base *file, bool resilient);
570 
get_tile_indexparam_sot571       ui16 get_tile_index() const { return Isot; }
get_payload_lengthparam_sot572       ui32 get_payload_length() const { return Psot > 0 ? Psot - 12 : 0; }
get_tile_part_indexparam_sot573       ui8  get_tile_part_index() const { return TPsot; }
get_num_tile_partsparam_sot574       ui8  get_num_tile_parts() const { return TNsot; }
575 
576     private:
577       ui16 Lsot;
578       ui16 Isot;
579       ui32 Psot;
580       ui8 TPsot;
581       ui8 TNsot;
582     };
583 
584     ///////////////////////////////////////////////////////////////////////////
585     //
586     //
587     //
588     //
589     //
590     ///////////////////////////////////////////////////////////////////////////
591     struct param_tlm
592     {
593       struct Ttlm_Ptlm_pair
594       {
595         ui16 Ttlm;
596         ui32 Ptlm;
597       };
598 
599     public:
param_tlmparam_tlm600       param_tlm() { pairs = NULL; num_pairs = 0; next_pair_index = 0; };
601       void init(int num_pairs, Ttlm_Ptlm_pair* store);
602 
603       void set_next_pair(ui16 Ttlm, ui32 Ptlm);
604       bool write(outfile_base *file);
605 
606     private:
607       ui16 Ltlm;
608       ui8 Ztlm;
609       ui8 Stlm;
610       Ttlm_Ptlm_pair* pairs;
611       int num_pairs;
612       int next_pair_index;
613 
614     };
615   }
616 }
617 
618 #endif // !OJPH_PARAMS_LOCAL_H
619