MODULE JPEG2000DecoderUtil;
(* Part of the JPEG2000 decoder implementation *)
(* Partially based on the JJ2000 reference implementation of EPF Lausanne (http://jj2000.epfl.ch) *)
(* Contains utility types *)
IMPORT SYSTEM, KernelLog;
CONST
(* Number of bits used to represent a LONGINT *)
LONGINT_BITS* = SYSTEM.VAL(LONGINT, SYSTEM.SIZEOF(LONGINT) * 8);
(* Mask to set / test for the sign bit of a LONGINT, i.e. the MSB of a LONGINT register *)
LONGINT_SIGN_BIT* = {LONGINT_BITS - 1};
SWAP_MASK* = SYSTEM.VAL(LONGINT, -1);
(* --- Subband types --- *)
SUB_LL* = 0;
SUB_HL* = 1;
SUB_LH* = 2;
SUB_HH* = 3;
(* --- Subband types --- *)
(* --- Constants used by FileInputStream --- *)
DEFAULT_READER_SIZE = 65536;
WRITE_ERROR = 2907;
(* --- END Constants used by FileInputStream --- *)
TYPE
(* --- Utility types --- *)
LongIntArrayPtr* = POINTER TO ARRAY OF LONGINT;
LongInt2DArrayPtr* = POINTER TO ARRAY OF LongIntArrayPtr;
LongInt3DArrayPtr* = POINTER TO ARRAY OF LongInt2DArrayPtr;
LongInt4DArrayPtr* = POINTER TO ARRAY OF LongInt3DArrayPtr;
ByteArrayPtr* = POINTER TO ARRAY OF CHAR;
RealArrayPtr* = POINTER TO ARRAY OF REAL;
SetArrayPtr* = POINTER TO ARRAY OF SET;
ByteArrayReader* = OBJECT
VAR
arr : ByteArrayPtr; (* A pointer to the raw coded code-block data that will be processed *)
arrPos, (* The byte position in the raw coded code-block data *)
dataLen : LONGINT; (* The number of total bytes of this array (adding the offset) *)
PROCEDURE &InitNew* (arr : ByteArrayPtr; offset, len : LONGINT);
BEGIN
ReInit(arr, offset, len);
END InitNew;
PROCEDURE ReInit* (arr : ByteArrayPtr; offset, len : LONGINT);
BEGIN
SELF.arr := arr;
arrPos := offset;
dataLen := offset + len;
END ReInit;
(**
Sets the byte array from which to read data.
*)
PROCEDURE SetArray* (arr : ByteArrayPtr; offset, len : LONGINT);
BEGIN
(*
NOTE:
If a parameter has an "illegal" value, we don't set it.
But we set those values which are in a valid range. In this way only some of the state
variables may be altered. E.g., this could be desirable when a data array has multiple
segments.
*)
IF arr # NIL THEN
SELF.arr := arr;
dataLen := offset + len;
arrPos := offset;
ELSIF offset < 0 THEN
arrPos := dataLen;
INC(dataLen, len);
ELSE
dataLen := offset + len;
arrPos := offset;
END;
END SetArray;
(**
Reads the next byte in the internal array. If no (more)
data is available then -1 is returned.
*)
PROCEDURE Read* () : LONGINT;
BEGIN
IF (arr = NIL) OR (arrPos >= dataLen) THEN
(* End of array *)
RETURN -1;
ELSE
INC(arrPos);
RETURN ORD(arr[arrPos - 1]);
END;
END Read;
END ByteArrayReader;
(**
An interface for objects that are sources for single bits (e.g. of a stream).
*)
BitSource* = OBJECT
VAR
PROCEDURE NextBit* () : LONGINT;
END NextBit;
END BitSource;
(**
Returns single bits (instead of bytes as ByteArrayReader).
NOTE: This object is specially tailored for the use in an
entropy decoder.
*)
DataBitReader* = OBJECT(BitSource)
VAR
br : ByteArrayReader;
curByte : LONGINT;
curBytePos : LONGINT;
PROCEDURE &InitNew* (br : ByteArrayReader);
BEGIN
ReInit(br);
END InitNew;
PROCEDURE ReInit* (br : ByteArrayReader);
BEGIN
SELF.br := br;
curBytePos := 0;
curByte := 0;
END ReInit;
PROCEDURE NextBit* () : LONGINT;
BEGIN
IF curBytePos = 0 THEN
(* Do bit unstuffing? *)
IF SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, curByte) * {0..7}) = 0FFH THEN
curBytePos := 7;
ELSE
curBytePos := 8;
END;
curByte := br.Read();
END;
DEC(curBytePos);
RETURN SYSTEM.VAL(LONGINT,
SYSTEM.VAL(SET, SYSTEM.LSH(curByte, -curBytePos))
* {0}
);
END NextBit;
(**
Starts a new (raw/bypass) segment.
*)
PROCEDURE NextSegment* (data : ByteArrayPtr; offset, len : LONGINT);
BEGIN
br.SetArray(data, offset, len);
curBytePos := 0;
curByte := 0;
END NextSegment;
(**
Checks the byte padding. This procedure checks the remainder of
each raw segment and tries to detect errors
*)
PROCEDURE CheckBytePadding* () : BOOLEAN;
VAR
seq : LONGINT;
BEGIN
(* If there are no bits left and the current byte is 0xFF then there is a next byte with bit stuffing *)
IF (curBytePos <= 0) & (curByte = 0FFH) THEN
curByte := br.Read();
curBytePos := 7;
END;
(*
(1)
The last bits that have not been read yet must be an
alternating sequence of 0's and 1's, starting with a 0
*)
IF curBytePos > 0 THEN
seq := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, curByte) * SYSTEM.VAL(SET, SYSTEM.LSH(1, curBytePos) - 1));
IF seq # SYSTEM.LSH(55H, curBytePos - 8) THEN
RETURN FALSE;
END;
END;
(*
(2)
We must have already reached the last byte in the terminated segment, unless
the last bit read is the LSB of a 0xFF byte in which case an encoder may have
added an extra byte smaller than 0x80
*)
IF curByte # -1 THEN
IF (curByte = 0FFH) & (curBytePos = 1) THEN
IF br.Read() >= 80H THEN
RETURN FALSE;
END;
ELSIF br.Read() # -1 THEN
RETURN FALSE;
END;
END;
RETURN TRUE;
END CheckBytePadding;
END DataBitReader;
(* --- END Utility types --- *)
(**
Holds a pointer to compressed image data for a specific code-block. Some
information on the compressed data is contained as well.
*)
CodedCblk* = RECORD
cpasses* : LONGINT; (* Number of coding passes for this coded code-block *)
nseg* : LONGINT; (* The number of of segments for this coded code-block *)
segLen* : LongIntArrayPtr; (* The length of each segment of raw data for this coded code-block and the included coding passes *)
dataOffset* : LONGINT; (* The offset into the data array where the data for this coded code-block begins *)
dataLen* : LONGINT; (* Thel length of all segments together *)
data* : ByteArrayPtr; (* The compressed image data *)
END;
(**
Contains information about a (data) block.
*)
BlkInfo* = OBJECT
VAR
ulx*, uly* : LONGINT; (* The blocks upper-left corner x and y coordinates. these coordinates are relative to the reference grid *)
height*, width* : LONGINT; (* The width and height of the block (in samples) *)
END BlkInfo;
(**
Contains information about a specific code-block.
*)
CblkInfo* = OBJECT(BlkInfo)
VAR
(* TODO: Do we really need those two fields? *)
ulsx*, ulsy* : LONGINT; (* The code-blocks upper-left corner x and y coordinates. See SubbandInfo object for more information *)
truncpt* : LONGINT; (* The truncation point for the code-block, i.e. the total number of coding passes read for the code-block up to now *)
cpasseslyr* : LongIntArrayPtr; (* The number of coding passes for each layer *)
zerobp* : LONGINT; (* The number of zero bit-planes for this code-block *)
curbp* : LONGINT; (* The index of the least significant bit-plane which has been decoded (31 means that no bit-plane has been decoded yet) *)
datalenlyr* : LongIntArrayPtr; (* The data length for the code-block in each layer *)
subbinfo* : SubbandInfo; (* A reference to the subband info object which describes the subband this code-block belongs to *)
index* : LONGINT; (* Index of the code-block in the subband (numbered in raster order) *)
END CblkInfo;
(**
Contains information about a specific subband (of a certain tile-component).
NOTE: A subband may also be one that is not present yet, i.e. will be created through
(inverse) wavelet transformation (that means that a LL subband of the next higher
resolution level has been created).
*)
SubbandInfo* = OBJECT
VAR
type* : LONGINT; (* The subband type: SUB_LL, SUB_HL, SUB_LH or SUB_HH *)
index* : LONGINT; (* The index of the subband relativ to the other subbands of the same decomposition level *)
(* -> SUB_LL: 0, SUB_HL: 0, SUB_LH: 1, SUB_HH: 2 *)
ulcx*, ulcy* : LONGINT; (* The upper left horizontal and vertical coordinates relative to the component this subband belongs to *)
(* NOTE: These are the actual coordinates of the subband in the component domain of the image *)
ulsx*, ulsy* : LONGINT; (* The upper left horizontal and vertical coordinates of the subband *)
(* NOTE: These coordinates ensure that the subband is placed at the right position for the filtering process. If the image consists of only 1 tile then these coordinates are almost the same as ulcx & ulcy (except that we usually start at the origin here)*)
width*, height* : LONGINT; (* The width and height of the subband (in component samples) *)
nblocksx*, nblocksy* : LONGINT; (* The number of code-blocks (in horizontal and vertical direction) contained in this subband *)
magbits* : LONGINT; (* The maximum number of magnitude bits for any coefficient in this subband (this number plays a role in the entropy decoding and dequantization process *)
component* : LONGINT; (* The index of the component to which the subband belongs to *)
reslevel* : LONGINT; (* The resolution level to which the subband belongs to *)
declevel* : LONGINT; (* The decomposition level this subband belongs to *)
END SubbandInfo;
(**
Used as node in a tag tree.
*)
TreeNode = RECORD
value : LONGINT;
valid : BOOLEAN;
END;
(**
This object is used to code 2 dimensional arrays of integer values by using a tag tree
(refer to the JPEG2000 Specification, Annex B.10.2 for details). Partial tag trees are
also covered.
*)
TagTree* = OBJECT
VAR
(* The index of the maximum level of this tag tree = LEN(nodes^) - 1 *)
maxLevel : LONGINT;
maxX : LONGINT; (* max. index in x direction (equals max. horizontal index of coded matrix) *)
maxY : LONGINT; (* max. index in y direction (equals max. vertical index of coded matrix) *)
(*
1st dim.: level (top node is located at level 0)
2nd dim.: node index
*)
nodes : POINTER TO ARRAY OF POINTER TO ARRAY OF TreeNode;
src : BitSource; (* A reference to the bit source *)
PROCEDURE &InitNew*(ncblx, ncbly : LONGINT; src : BitSource);
VAR
x, y, i, j, prod : LONGINT;
BEGIN
maxX := ncblx - 1;
maxY := ncbly - 1;
i := 1;
maxLevel := 0;
(* The maximum value of height and width determines the amount of levels *)
IF ncblx > ncbly THEN
WHILE i < ncblx DO
INC(maxLevel);
i := SYSTEM.LSH(i, 1);
END;
ELSE
WHILE i < ncbly DO
INC(maxLevel);
i := SYSTEM.LSH(i, 1);
END;
END;
NEW(nodes, maxLevel+1);
x := ncblx;
y := ncbly;
(* For every level we have to compute the number of nodes, allocate the needed space, and initialize each node *)
FOR i := maxLevel TO 0 BY -1 DO
prod := x*y;
NEW(nodes[i], prod);
FOR j := 0 TO prod - 1 DO
nodes[i][j].value := 0;
nodes[i][j].valid := FALSE;
END;
x := SYSTEM.LSH(x, -1) + SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, x) * {0});
y := SYSTEM.LSH(y, -1) + SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, y) * {0});
END;
SELF.src := src;
END InitNew;
(**
Update the tag tree
x: The horizontal index of the node that needs to be updated
y: The vertical index of the node that needs to be updated
threshold: This value determines the threshold to which node values
maybe incremented in the current procedure call (needed for
partial tag trees). Actually each node value may be incremented by one
if the current value is lower than or equal to the threshold
RETURN: TRUE, if the update operation succeeded, FALSE otherwise
*)
PROCEDURE Update*(x, y : LONGINT; threshold : LONGINT) : BOOLEAN;
VAR
level, shift, bit, idx, nextIdx, prevIdx : LONGINT;
BEGIN
(*
Precondition : There is a level lev <= maxLevel, and an index idx such that
nodes[lev][idx].valid = FALSE and the node represented by nodes[lev][idx]
is parent of the node represented by nodes[maxLevel][<index of node (x,y) at maxLevel>]
in the tag tree. maxLevel is assumed to be >= 0.
Postcondition : The node described in the precondition has either become valid or its value has increased, i.e.
the value may be increased later (we use a lazy-update strategy)
Invariant(s): num of valid nodes = num of '1's read from the stream
num of value incrementations = num of '0's read from the stream
If node(x).valid then node(y).valid for all y: node(y)=ancestor(node(x))
*)
IF (x > maxX) OR (y > maxY) THEN
(* Out of bounds *)
KernelLog.String("ERROR (TagTree.Update): Indices for tag tree are too large");
KernelLog.Ln();
RETURN FALSE;
END;
(* Init value to 'first' - 1 ('first' + 1 for shift)*)
level := -1;
shift := maxLevel+1; (* shift is needed to compute the correct index of the ancestors of x,y at each level *)
(* Skip all valid nodes *)
REPEAT
INC(level);
DEC(shift);
prevIdx := idx;
idx := SYSTEM.LSH(y, -shift) * (SYSTEM.LSH(maxX, -shift) + 1) + SYSTEM.LSH(x, -shift);
UNTIL (level >= maxLevel) OR ~nodes[level][idx].valid;
IF nodes[level][idx].valid THEN
(* Precondition not satisfied *)
KernelLog.String("ERROR: Precondition of TagTree.Update violated");
KernelLog.Ln();
RETURN FALSE;
END;
(* Restore tag tree condition for the current node *)
IF (level > 0) & (nodes[level-1][prevIdx].value > nodes[level][idx].value) THEN
nodes[level][idx].value := nodes[level-1][prevIdx].value;
END;
(* Traverse the tree and increment or validate the current node *)
WHILE (nodes[level][idx].value <= threshold) & (level < maxLevel) DO
bit := src.NextBit();
(* We need to have all values on the path to the node being updated *)
IF bit = 1 THEN
nodes[level][idx].valid := TRUE;
INC(level);
DEC(shift);
nextIdx := SYSTEM.LSH(y, -shift) * (SYSTEM.LSH(maxX, -shift) + 1) + SYSTEM.LSH(x, -shift);
nodes[level][nextIdx].value := nodes[level-1][idx].value;
idx := nextIdx;
ELSE
INC(nodes[level][idx].value);
END;
END;
WHILE ~nodes[level][idx].valid & (nodes[level][idx].value <= threshold) DO
(*
Since we are (or at least should be) at the maximum level we need to increment
as long as '0's are following or the threshold has been passed
*)
bit := src.NextBit();
IF bit = 1 THEN
nodes[level][idx].valid := TRUE;
ELSE
INC(nodes[level][idx].value);
END;
END;
RETURN TRUE;
END Update;
(**
Test if the node (x,y) is valid (at the max. level)
RETURN: TRUE, if the node (x,y) is valid (at the max. level), FALSE otherwise
*)
PROCEDURE IsValid* (x, y : LONGINT) : BOOLEAN;
BEGIN
IF (x > maxX) OR (y > maxY) THEN
KernelLog.String("ERROR (TagTree.IsValid): Index out of bounds");
RETURN FALSE;
END;
RETURN nodes[maxLevel][y * (maxX + 1) + x].valid;
END IsValid;
(*
SUMMARY: Returns the current value of node (x,y) (at the max. level). NOTE: This value may
be 0 even if the current value should be higher; that's because we use a lazy-update
strategy. So the returned value only makes sense if the the node is valid.
RETURN: The current value of the node (x,y) (or 0 -> see SUMMARY above), -1 if an error occurred
*)
PROCEDURE CurrentVal* (x, y : LONGINT) : LONGINT;
BEGIN
IF (x > maxX) OR (y > maxY) THEN
KernelLog.String("ERROR (TagTree.CurrentVal): Index out of bounds");
RETURN -1;
END;
RETURN nodes[maxLevel][y * (maxX + 1) + x].value;
END CurrentVal;
END TagTree;
(*
(**
This object is used in the same way as Rider0 is used in the Files module.
The object declaration was copied and adapted from the Files module.
*)
RiderWrapper = OBJECT
VAR
r: Files.Rider;
PROCEDURE Send(VAR buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT);
BEGIN
r.file.WriteBytes(r, buf, ofs, len);
IF propagate THEN r.file.Update END;
IF r.res = 0 THEN res := Streams.Ok ELSE res := WRITE_ERROR (* not all bytes written *) END
END Send;
PROCEDURE Receive(VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT);
BEGIN
r.file.ReadBytes(r, buf, ofs, size);
len := size - r.res;
IF len >= min THEN res := Streams.Ok ELSE res := Streams.EOF (* end of file *) END
END Receive;
END RiderWrapper;
FileInputStream* = OBJECT(Streams.Reader) (* not shareable between multiple processes *)
VAR
r: RiderWrapper;
PROCEDURE &InitFileStream* (f : Files.File; pos : LONGINT);
BEGIN
NEW(r); f.Set(r.r, pos);
InitReader(r.Receive, DEFAULT_READER_SIZE)
END InitFileStream;
END FileInputStream;
*)
(**
Holds the options for the decoder at instantiation time.
*)
DecoderOptions* = OBJECT
VAR
(* Component-specific options *)
(* NOTE: Some components may not have any options *)
crOpt* : CodestreamReaderOptions;
edOpt* : EntropyDecoderOptions;
roiOpt* : ROIDescalerOptions;
deqOpt* : DequantizerOptions;
invDWTOpt* : InverseDWTOptions;
invMCTOpt* : InverseMCTOptions;
END DecoderOptions;
(**
An 'abstract' object which shall be implemted by option objects,
providing settings for each component in the (main) decoding chain.
*)
ComponentOptions* = OBJECT
VAR
(**
The identifier of the component that this options apply to.
*)
component* : LONGINT;
END ComponentOptions;
CodestreamReaderOptions* = OBJECT(ComponentOptions)
VAR
(**
If TRUE, then comments found in the COM segments will be printed.
*)
printComments* : BOOLEAN;
END CodestreamReaderOptions;
EntropyDecoderOptions* = OBJECT(ComponentOptions)
VAR
(**
Specifies if error detection should be performed by the entropy decoder
engine. If errors are detected they will be concealed and the resulting
distortion will be less important.
NOTE: Errors can only be detected if the encoder that generated the data
included error resilience information.
*)
concealError* : BOOLEAN;
END EntropyDecoderOptions;
ROIDescalerOptions* = OBJECT(ComponentOptions)
VAR
(**
This argument makes sure that no ROI de-scaling is performed.
Decompression is done like there is no ROI in the image
*)
noROI* : BOOLEAN;
END ROIDescalerOptions;
(**
Just a marker interface, presently
*)
DequantizerOptions* = OBJECT(ComponentOptions)
END DequantizerOptions;
InverseDWTOptions* = OBJECT(ComponentOptions)
VAR
filterRev* : LONGINT; (** The reversible filter to use *)
filterIrrev* : LONGINT; (** The irreversible filter to use *)
END InverseDWTOptions;
InverseMCTOptions* = OBJECT(ComponentOptions)
VAR
(**
Indicates if the internal buffers shall be conserved or not,
if a change to the rebuild mode is done. If this is set to
TRUE, then at each rebuild a new buffer for the components
is allocated (that is, if a multiple component trans. is used at all)
*)
nonRebuildBuffer* : BOOLEAN;
END InverseMCTOptions;
(* RETURN: The logarithm (base 2) of the largest power of 2 <= x *)
PROCEDURE Log2Floor* (x : LONGINT) : LONGINT;
VAR
logVal : LONGINT;
BEGIN
(* NOTE: We don't check wether x >= 1 *)
logVal := 0;
WHILE x > 1 DO
x := SYSTEM.LSH(x, -1);
INC(logVal);
END;
RETURN logVal;
END Log2Floor;
(* --- END Utility functions --- *)
(**
Returns the index of the subband given the subband type.
SUB_LL is alone in it's domain, so its index is 0.
The others constitute a group of 3 and are indexed in raster order
(i.e. raster order relative to the reference grid)
*)
PROCEDURE SubbandToSubbandIndex* (subband : LONGINT) : LONGINT;
BEGIN
CASE subband OF
| SUB_LL :
RETURN 0;
|
SUB_HL :
RETURN 0;
|
SUB_LH :
RETURN 1;
| SUB_HH :
RETURN 2;
ELSE
RETURN -1;
END;
END SubbandToSubbandIndex;
(**
The inverse transformation to SubbandToSubbandIndex, except that
we need to know the resolution level, since the whole operation is not
bijective.
*)
PROCEDURE SubbandIndexToSubband* (reslevel, subbIndex : LONGINT) : LONGINT;
BEGIN
IF reslevel = 0 THEN
ASSERT(subbIndex = 0);
RETURN SUB_LL;
ELSE
CASE subbIndex OF
| 0 :
RETURN SUB_HL;
| 1 :
RETURN SUB_LH;
| 2 :
RETURN SUB_HH;
ELSE
(* Should never get here *)
HALT(99);
END;
END;
END SubbandIndexToSubband;
END JPEG2000DecoderUtil.
SystemTools.Free JPEG2000Util~