/*
* Copyright 2006-2012 ICEsoft Technologies Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS
* IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.icepdf.core.pobjects.functions;
import org.icepdf.core.pobjects.Dictionary;
import org.icepdf.core.pobjects.Stream;
import java.util.Vector;
import java.util.logging.Logger;
import java.util.logging.Level;
/**
* <p>This class <code>Function_0</code> represents a generic Type 0, sampled function
* type. Type 0 functions use a sequence of sampled values (contained in a stream)
* to produce an approximation for function shose domains and ranges are bounded.
* The samples are organized as an m-dimensional table in which each entry has n
* components. </p>
* <p/>
* <p>Sampled functiosn are highly general and offer reasonablly accurate
* representations of arbitrary analytic functions at low expense. The
* dimensionality of a sampled function is restricted only by the implementation
* limits.</p>
*
* @see Function
* @since 1.0
*/
public class Function_0 extends Function {
private static final Logger logger =
Logger.getLogger(Function_0.class.toString());
// An array of m positive integers specifying the number of samples in each
// input dimension of the sample table.
private int size[];
// The number of bits used to represent each sample. If the function has
// multiple output values, each one occupies BitsPerSample bits. Valid
// values are 1,2,4,8,12,16,24, and 32.
private int bitspersample;
// The order of interpolation between samples. Valid values are 1 and 3,
// specifying linear and cubic spline interpolation, respectively. Default 1
private int order;
// An array of 2 x m numbers specifying the linear mapping of input values
// into the domain of the function's sample table. Default value:
// [0 (size<sub>0</sub>-1) 0 size<sub>1</sub> ...].
private float encode[];
// An array of 2 x n numbers specifying the linear mapping of sample values
// into the range the range appropriate for the function's output values.
// Default same as Range.
private float decode[];
// associated stream bytes, comes from dictionary
private byte bytes[];
/**
* Creates a new instance of a type 0 function.
*
* @param d function's dictionary.
*/
Function_0(Dictionary d) {
// initiate, domain and range
super(d);
Vector s = (Vector) d.getObject("Size");
// setup size array, each entry represents the number of samples for
// each input dimension.
size = new int[s.size()];
for (int i = 0; i < s.size(); i++) {
size[i] = (int) (((Number) s.elementAt(i)).floatValue());
}
// setup bitspersample array, each entry represents the number of bits used
// for each sample
bitspersample = d.getInt("BitsPerSample");
// setup of encode table, specifies the linear mapping of input values
// into the domain of the function's sample table.
Vector enc = (Vector) d.getObject("Encode");
encode = new float[size.length * 2];
if (enc != null) {
for (int i = 0; i < size.length * 2; i++) {
encode[i] = ((Number) enc.elementAt(i)).floatValue();
}
} else {
// encoding is optional, so fill up encode area with uniform
// mapping of 0,size[0]-1, 0,size[1]-1, 0,size[2]-1 which is
// the default value which is defined in the spec.
for (int i = 0; i < size.length; i++) {
encode[2 * i] = 0;
encode[2 * i + 1] = size[i] - 1;
}
}
// setup decode, an array of 2 x n numbers specifying the linear mapping
// of sample values into the range appropriate for the function's output values.
Vector dec = (Vector) d.getObject("Decode");
decode = new float[range.length];
if (dec != null) {
for (int i = 0; i < range.length; i++) {
decode[i] = ((Number) dec.elementAt(i)).floatValue();
}
} else {
// deocode is optional, so we should copy range as a default values
System.arraycopy(range, 0, decode, 0, range.length);
// for (int i = 0; i < range.length; i++) {
// decode[i] = range[i];
// }
}
// lastly get the stream byte data if any.
Stream stream = (Stream) d;
bytes = stream.getBytes();
}
/**
* Calculates the y values for the given x values using a sampled function.
*
* @param x array of input values m.
* @return array of ouput value n.
*/
public float[] calculate(float[] x) {
// length of output array
int n = range.length / 2;
// ready output array
float y[] = new float[n];
// work throw all input data and store in y[]
try {
for (int i = 0; i < x.length; i++) {
// clip input value appropriately for the given domain
// xi' = min (max(xi, Domain2i), Domain2i+1)
x[i] = Math.min(Math.max(x[i], domain[2 * i]), domain[2 * i + 1]);
// find the encoded value
// ei = intermolate (xi', Domain2i, Domain2i+1, Encode2i, Encode2i+1)
float e = interpolate(x[i], domain[2 * i], domain[2 * i + 1],
encode[2 * i], encode[2 * i + 1]);
// clip to the size of the sampled table in that dimension:
// ei' = min (max(ei, 0), Sizei-1)
e = Math.min(Math.max(e, 0), size[i] - 1);
// pretty sure that e1 and e2 are used to for a bilinear interpolation?
// Output values are are caculated from the nearest surrounding values
// in the sample table in the sample table.
int e1 = (int) Math.floor(e);
int e2 = (int) Math.ceil(e);
int index;
// Calculate the final output values
for (int j = 0; j < n; j++) {
// find nearest surrounding values in the sample table
int b1 = ((int) bytes[(int) (e1 * n + j)]) & 255;
int b2 = ((int) bytes[(int) (e2 * n + j)]) & 255;
// get the average
float r = ((float) b1 + (float) b2) / 2;
// interplate to get output values
r = interpolate(r, 0f, (float) Math.pow(2, bitspersample) -
1, decode[2 * j], decode[2 * j + 1]);
// finally, decoded values are clipped ot the range
// yj = min(max(rj', Range2j), Range2j+1)
r = Math.min(Math.max(r, range[2 * j]), range[2 * j + 1]);
index = i * n + j;
// make sure we y can contain the calcualated r value
if (index < y.length) {
y[index] = r;
}
}
}
}
catch (Exception e) {
logger.log(Level.WARNING, "Error calculating function 0 values", e);
}
return y;
}
}