/*
* #%L
* Fork of JAI Image I/O Tools.
* %%
* Copyright (C) 2008 - 2014 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are
* those of the authors and should not be interpreted as representing official
* policies, either expressed or implied, of any organization.
* #L%
*/
/*
* $RCSfile: TagTreeEncoder.java,v $
* $Revision: 1.1 $
* $Date: 2005/02/11 05:02:03 $
* $State: Exp $
*
* Class: TagTreeEncoder
*
* Description: Encoder of tag trees
*
*
*
* COPYRIGHT:
*
* This software module was originally developed by Raphaël Grosbois and
* Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
* Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
* Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
* Centre France S.A) in the course of development of the JPEG2000
* standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
* software module is an implementation of a part of the JPEG 2000
* Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
* Systems AB and Canon Research Centre France S.A (collectively JJ2000
* Partners) agree not to assert against ISO/IEC and users of the JPEG
* 2000 Standard (Users) any of their rights under the copyright, not
* including other intellectual property rights, for this software module
* with respect to the usage by ISO/IEC and Users of this software module
* or modifications thereof for use in hardware or software products
* claiming conformance to the JPEG 2000 Standard. Those intending to use
* this software module in hardware or software products are advised that
* their use may infringe existing patents. The original developers of
* this software module, JJ2000 Partners and ISO/IEC assume no liability
* for use of this software module or modifications thereof. No license
* or right to this software module is granted for non JPEG 2000 Standard
* conforming products. JJ2000 Partners have full right to use this
* software module for his/her own purpose, assign or donate this
* software module to any third party and to inhibit third parties from
* using this software module for non JPEG 2000 Standard conforming
* products. This copyright notice must be included in all copies or
* derivative works of this software module.
*
* Copyright (c) 1999/2000 JJ2000 Partners.
*
*
*
*/
package jj2000.j2k.codestream.writer;
import jj2000.j2k.util.*;
import jj2000.j2k.io.*;
import java.io.*;
/**
* This class implements the tag tree encoder. A tag tree codes a 2D
* matrix of integer elements in an efficient way. The encoding
* procedure 'encode()' codes information about a value of the matrix,
* given a threshold. The procedure encodes the sufficient information
* to identify whether or not the value is greater than or equal to
* the threshold.
*
* <P>The tag tree saves encoded information to a BitOutputBuffer.
*
* <P>A particular and useful property of tag trees is that it is
* possible to change a value of the matrix, provided both new and old
* values of the element are both greater than or equal to the largest
* threshold which has yet been supplied to the coding procedure
* 'encode()'. This property can be exploited through the 'setValue()'
* method.
*
* <P>This class allows saving the state of the tree at any point and
* restoring it at a later time, by calling save() and restore().
*
* <P>A tag tree can also be reused, or restarted, if one of the
* reset() methods is called.
*
* <P>The TagTreeDecoder class implements the tag tree decoder.
*
* <P>Tag trees that have one dimension, or both, as 0 are allowed for
* convenience. Of course no values can be set or coded in such cases.
*
* @see BitOutputBuffer
*
* @see jj2000.j2k.codestream.reader.TagTreeDecoder
* */
public class TagTreeEncoder {
/** The horizontal dimension of the base level */
protected int w;
/** The vertical dimensions of the base level */
protected int h;
/** The number of levels in the tag tree */
protected int lvls;
/** The tag tree values. The first index is the level, starting at
* level 0 (leafs). The second index is the element within the
* level, in lexicographical order. */
protected int treeV[][];
/** The tag tree state. The first index is the level, starting at
* level 0 (leafs). The second index is the element within the
* level, in lexicographical order. */
protected int treeS[][];
/** The saved tag tree values. The first index is the level,
* starting at level 0 (leafs). The second index is the element
* within the level, in lexicographical order. */
protected int treeVbak[][];
/** The saved tag tree state. The first index is the level, starting at
* level 0 (leafs). The second index is the element within the
* level, in lexicographical order. */
protected int treeSbak[][];
/** The saved state. If true the values and states of the tree
* have been saved since the creation or last reset. */
protected boolean saved;
/**
* Creates a tag tree encoder with 'w' elements along the
* horizontal dimension and 'h' elements along the vertical
* direction. The total number of elements is thus 'vdim' x
* 'hdim'.
*
* <P>The values of all elements are initialized to Integer.MAX_VALUE.
*
* @param h The number of elements along the horizontal direction.
*
* @param w The number of elements along the vertical direction.
*
*
* */
public TagTreeEncoder(int h, int w) {
int k;
// Check arguments
if ( w < 0 || h < 0 ) {
throw new IllegalArgumentException();
}
// Initialize elements
init(w,h);
// Set values to max
for (k = treeV.length-1; k >= 0; k--) {
ArrayUtil.intArraySet(treeV[k],Integer.MAX_VALUE);
}
}
/**
* Creates a tag tree encoder with 'w' elements along the
* horizontal dimension and 'h' elements along the vertical
* direction. The total number of elements is thus 'vdim' x
* 'hdim'. The values of the leafs in the tag tree are initialized
* to the values of the 'val' array.
*
* <P>The values in the 'val' array are supposed to appear in
* lexicographical order, starting at index 0.
*
* @param h The number of elements along the horizontal direction.
*
* @param w The number of elements along the vertical direction.
*
* @param val The values with which initialize the leafs of the
* tag tree.
*
*
* */
public TagTreeEncoder(int h, int w, int val[]) {
int k;
// Check arguments
if ( w < 0 || h < 0 || val.length < w*h ) {
throw new IllegalArgumentException();
}
// Initialize elements
init(w,h);
// Update leaf values
for (k=w*h-1; k>=0; k--) {
treeV[0][k]=val[k];
}
// Calculate values at other levels
recalcTreeV();
}
/**
* Returns the number of leafs along the horizontal direction.
*
* @return The number of leafs along the horizontal direction.
*
*
* */
public final int getWidth() {
return w;
}
/**
* Returns the number of leafs along the vertical direction.
*
* @return The number of leafs along the vertical direction.
*
*
* */
public final int getHeight() {
return h;
}
/**
* Initializes the variables of this class, given the dimensions
* at the base level (leaf level). All the state ('treeS' array)
* and values ('treeV' array) are intialized to 0. This method is
* called by the constructors.
*
* @param w The number of elements along the vertical direction.
*
* @param h The number of elements along the horizontal direction.
*
*
* */
private void init(int w, int h) {
int i;
// Initialize dimensions
this.w = w;
this.h = h;
// Calculate the number of levels
if (w == 0 || h == 0) {
lvls = 0;
}
else {
lvls = 1;
while (h != 1 || w != 1) { // Loop until we reach root
w = (w+1)>>1;
h = (h+1)>>1;
lvls++;
}
}
// Allocate tree values and states
// (no need to initialize to 0 since it's the default)
treeV = new int[lvls][];
treeS = new int[lvls][];
w = this.w;
h = this.h;
for (i=0; i<lvls; i++) {
treeV[i] = new int[h*w];
treeS[i] = new int[h*w];
w = (w+1)>>1;
h = (h+1)>>1;
}
}
/**
* Recalculates the values of the elements in the tag tree, in
* levels 1 and up, based on the values of the leafs (level 0).
*
*
* */
private void recalcTreeV() {
int m,n,bi,lw,tm1,tm2,lh,k;
// Loop on all other levels, updating minimum
for (k=0; k<lvls-1; k++) {
// Visit all elements in level
lw = (w+(1<<k)-1)>>k;
lh = (h+(1<<k)-1)>>k;
for (m=((lh>>1)<<1)-2;m>=0;m-=2) { // All quads with 2 lines
for (n=((lw>>1)<<1)-2;n>=0;n-=2) { // All quads with 2 columns
// Take minimum of 4 elements and put it in higher
// level
bi = m*lw+n;
tm1 = (treeV[k][bi] < treeV[k][bi+1]) ?
treeV[k][bi] : treeV[k][bi+1];
tm2 = (treeV[k][bi+lw] < treeV[k][bi+lw+1]) ?
treeV[k][bi+lw] : treeV[k][bi+lw+1];
treeV[k+1][(m>>1)*((lw+1)>>1)+(n>>1)] =
tm1 < tm2 ? tm1 : tm2;
}
// Now we may have quad with 1 column, 2 lines
if (lw%2 != 0) {
n = ((lw>>1)<<1);
// Take minimum of 2 elements and put it in higher
// level
bi = m*lw+n;
treeV[k+1][(m>>1)*((lw+1)>>1)+(n>>1)] =
(treeV[k][bi] < treeV[k][bi+lw]) ?
treeV[k][bi] : treeV[k][bi+lw];
}
}
// Now we may have quads with 1 line, 2 or 1 columns
if (lh%2 != 0) {
m = ((lh>>1)<<1);
for (n=((lw>>1)<<1)-2;n>=0;n-=2) { // All quads with 2 columns
// Take minimum of 2 elements and put it in higher
// level
bi = m*lw+n;
treeV[k+1][(m>>1)*((lw+1)>>1)+(n>>1)] =
(treeV[k][bi] < treeV[k][bi+1]) ?
treeV[k][bi] : treeV[k][bi+1];
}
// Now we may have quad with 1 column, 1 line
if (lw%2 != 0) {
// Just copy the value
n = ((lw>>1)<<1);
treeV[k+1][(m>>1)*((lw+1)>>1)+(n>>1)] =
treeV[k][m*lw+n];
}
}
}
}
/**
* Changes the value of a leaf in the tag tree. The new and old
* values of the element must be not smaller than the largest
* threshold which has yet been supplied to 'encode()'.
*
* @param m The vertical index of the element.
*
* @param n The horizontal index of the element.
*
* @param v The new value of the element.
*
*
* */
public void setValue(int m, int n, int v) {
int k,idx;
// Check arguments
if (lvls == 0 || n < 0 || n >= w || v < treeS[lvls-1][0] ||
treeV[0][m*w+n] < treeS[lvls-1][0]) {
throw new IllegalArgumentException();
}
// Update the leaf value
treeV[0][m*w+n] = v;
// Update all parents
for (k=1; k<lvls; k++) {
idx = (m>>k)*((w+(1<<k)-1)>>k)+(n>>k);
if (v < treeV[k][idx]) {
// We need to update minimum and continue checking
// in higher levels
treeV[k][idx] = v;
}
else {
// We are done: v is equal or less to minimum
// in this level, no other minimums to update.
break;
}
}
}
/**
* Sets the values of the leafs to the new set of values and
* updates the tag tree accordingly. No leaf can change its value
* if either the new or old value is smaller than largest
* threshold which has yet been supplied to 'encode()'. However
* such a leaf can keep its old value (i.e. new and old value must
* be identical.
*
* <P>This method is more efficient than the setValue() method if
* a large proportion of the leafs change their value. Note that
* for leafs which don't have their value defined yet the value
* should be Integer.MAX_VALUE (which is the default
* initialization value).
*
* @param val The new values for the leafs, in lexicographical order.
*
* @see #setValue
*
*
* */
public void setValues(int val[]) {
int i,maxt;
if (lvls == 0) { // Can't set values on empty tree
throw new IllegalArgumentException();
}
// Check the values
maxt = treeS[lvls-1][0];
for (i=w*h-1; i>=0; i--) {
if ((treeV[0][i] < maxt || val[i] < maxt) &&
treeV[0][i] != val[i]) {
throw new IllegalArgumentException();
}
// Update leaf value
treeV[0][i] = val[i];
}
// Recalculate tree at other levels
recalcTreeV();
}
/**
* Encodes information for the specified element of the tree,
* given the threshold and sends it to the 'out' stream. The
* information that is coded is whether or not the value of the
* element is greater than or equal to the value of the threshold.
*
* @param m The vertical index of the element.
*
* @param n The horizontal index of the element.
*
* @param t The threshold to use for encoding. It must be non-negative.
*
* @param out The stream where to write the coded information.
*
*
* */
public void encode(int m, int n, int t, BitOutputBuffer out) {
int k,ts,idx,tmin;
// Check arguments
if (m >= h || n >= w || t < 0) {
throw new IllegalArgumentException();
}
// Initialize
k = lvls-1;
tmin = treeS[k][0];
// Loop on levels
while (true) {
// Index of element in level 'k'
idx = (m>>k)*((w+(1<<k)-1)>>k)+(n>>k);
// Cache state
ts = treeS[k][idx];
if (ts < tmin) {
ts = tmin;
}
while (t > ts) {
if (treeV[k][idx] > ts) {
out.writeBit(0); // Send '0' bit
}
else if (treeV[k][idx] == ts) {
out.writeBit(1); // Send '1' bit
}
else { // we are done: set ts and get out of this while
ts = t;
break;
}
// Increment of treeS[k][idx]
ts++;
}
// Update state
treeS[k][idx] = ts;
// Update tmin or terminate
if (k>0) {
tmin = ts < treeV[k][idx] ? ts : treeV[k][idx];
k--;
}
else {
// Terminate
return;
}
}
}
/**
* Saves the current values and state of the tree. Calling
* restore() restores the tag tree the saved state.
*
* @see #restore
*
*
* */
public void save() {
int k,i;
if (treeVbak == null) { // Nothing saved yet
// Allocate saved arrays
// treeV and treeS have the same dimensions
treeVbak = new int[lvls][];
treeSbak = new int[lvls][];
for (k=lvls-1 ; k >= 0; k--) {
treeVbak[k] = new int[treeV[k].length];
treeSbak[k] = new int[treeV[k].length];
}
}
// Copy the arrays
for (k=treeV.length-1 ; k >= 0; k--) {
System.arraycopy(treeV[k],0,treeVbak[k],0,treeV[k].length);
System.arraycopy(treeS[k],0,treeSbak[k],0,treeS[k].length);
}
// Set saved state
saved = true;
}
/**
* Restores the saved values and state of the tree. An
* IllegalArgumentException is thrown if the tree values and state
* have not been saved yet.
*
* @see #save
*
*
* */
public void restore() {
int k,i;
if (!saved) { // Nothing saved yet
throw new IllegalArgumentException();
}
// Copy the arrays
for (k=lvls-1 ; k >= 0; k--) {
System.arraycopy(treeVbak[k],0,treeV[k],0,treeV[k].length);
System.arraycopy(treeSbak[k],0,treeS[k],0,treeS[k].length);
}
}
/**
* Resets the tree values and state. All the values are set to
* Integer.MAX_VALUE and the states to 0.
*
*
* */
public void reset() {
int k;
// Set all values to Integer.MAX_VALUE
// and states to 0
for (k = lvls-1; k >= 0; k--) {
ArrayUtil.intArraySet(treeV[k],Integer.MAX_VALUE);
ArrayUtil.intArraySet(treeS[k],0);
}
// Invalidate saved tree
saved = false;
}
/**
* Resets the tree values and state. The values are set to the
* values in 'val'. The states are all set to 0.
*
* @param val The new values for the leafs, in lexicographical order.
*
*
* */
public void reset(int val[]) {
int k;
// Set values for leaf level
for (k=w*h-1; k>=0; k--) {
treeV[0][k] = val[k];
}
// Calculate values at other levels
recalcTreeV();
// Set all states to 0
for (k = lvls-1; k >= 0; k--) {
ArrayUtil.intArraySet(treeS[k],0);
}
// Invalidate saved tree
saved = false;
}
}