Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* $Id: FunctionType0.java,v 1.3 2007/12/20 18:33:35 rbair Exp $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview.function;
import java.io.IOException;
import java.nio.ByteBuffer;
import com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* A sampled function maps input values to output values by interpolating
* along a line or cubic between two known values.
*/
public class FunctionType0 extends PDFFunction {
/** the valid interpolation methods */
protected static final int LINEAR_INTERPOLATION = 1;
protected static final int CUBIC_INTERPOLATION = 3;
/** the size of each input dimension, as an array of m integers */
private int[] size;
/** the number of bits in each sample */
private int bitsPerSample;
/** the interpolation type, from the list above */
private int order = 1;
/** the optional encoding array, tells how to map input parameters to values */
private float[] encode;
/** the optional decoding array, tells how to map output parameters to values */
private float[] decode;
/**
* the actual samples, converted to integers. The first index is
* input values (from 0 to size[m - 1] * size[m - 2] * ... * size[0]),
* and the second is the output dimension within the sample (from 0 to n)
*/
private int[][] samples;
/** Creates a new instance of FunctionType0 */
protected FunctionType0() {
super (TYPE_0);
}
/** Read the function information from a PDF Object */
protected void parse(PDFObject obj) throws IOException {
// read the size array (required)
PDFObject sizeObj = obj.getDictRef("Size");
if (sizeObj == null) {
throw new PDFParseException("Size required for function type 0!");
}
PDFObject[] sizeAry = sizeObj.getArray();
int[] size = new int[sizeAry.length];
for (int i = 0; i < sizeAry.length; i++) {
size[i] = sizeAry[i].getIntValue();
}
setSize(size);
// read the # bits per sample (required)
PDFObject bpsObj = obj.getDictRef("BitsPerSample");
if (bpsObj == null) {
throw new PDFParseException("BitsPerSample required for function type 0!");
}
setBitsPerSample(bpsObj.getIntValue());
// read the order (optional)
PDFObject orderObj = obj.getDictRef("Order");
if (orderObj != null) {
setOrder(orderObj.getIntValue());
}
// read the encode array (optional)
PDFObject encodeObj = obj.getDictRef("Encode");
if (encodeObj != null) {
PDFObject[] encodeAry = encodeObj.getArray();
float[] encode = new float[encodeAry.length];
for (int i = 0; i < encodeAry.length; i++) {
encode[i] = encodeAry[i].getFloatValue();
}
setEncode(encode);
}
// read the decode array (optional)
PDFObject decodeObj = obj.getDictRef("Decode");
if (decodeObj != null) {
PDFObject[] decodeAry = decodeObj.getArray();
float[] decode = new float[decodeAry.length];
for (int i = 0; i < decodeAry.length; i++) {
decode[i] = decodeAry[i].getFloatValue();
}
setDecode(decode);
}
// finally, read the samples
setSamples(readSamples(obj.getStreamBuffer()));
}
/**
* Map from m input values to n output values.
* The number of inputs m must be exactly one half the size of the
* domain. The number of outputs should match one half the size of the
* range.
*
* @param inputs an array of m input values
* @param outputs an array of size n which will be filled
* with the output values, or null to return a new array
*/
protected void doFunction(float[] inputs, int inputOffset,
float[] outputs, int outputOffset)
{
// calculate the encoded values for each input
float[] encoded = new float[getNumInputs()];
for (int i = 0; i < getNumInputs(); i++) {
// encode -- interpolate(x, domain<2i>, domain<2i + 1>,
// encode<2i>, encode<2i + 1>)
encoded[i] = interpolate(inputs[i + inputOffset],
getDomain(2 * i),
getDomain((2 * i) + 1),
getEncode(2 * i),
getEncode((2 * i) + 1));
// clip to size of sample table -- min(max(e, 0), size - 1)
encoded[i] = Math.max(encoded[i], 0);
encoded[i] = Math.min(encoded[i], size[i] - 1);
}
// do some magic
for (int i = 0; i < getNumOutputs(); i++) {
if (getOrder() == 1) {
outputs[i + outputOffset] = multilinearInterpolate(encoded, i);
} else {
outputs[i + outputOffset] = multicubicInterpolate(encoded, i);
}
}
// now adjust the output to be within range
for (int i = 0; i < outputs.length; i++) {
// decode -- interpolate(r, 0, 2^bps - 1,
// decode<2i>, decode<2i + 1>)
outputs[i + outputOffset] = interpolate(outputs[i + outputOffset],
0,
(float) Math.pow(2, getBitsPerSample()) - 1,
getDecode(2 * i),
getDecode((2 * i) + 1));
}
}
/**
* Get the size of a given input dimension
*
* @param dimension the input dimension to get the size of
* @return the number of samples in the given dimension
*/
protected int getSize(int dimension) {
return size[dimension];
}
/**
* Set the size of all input dimensions
*/
protected void setSize(int[] size) {
this.size = size;
}
/**
* Get the number of bits per sample
*/
protected int getBitsPerSample() {
return bitsPerSample;
}
/**
* Set the number of bits per sample
*/
protected void setBitsPerSample(int bits) {
this.bitsPerSample = bits;
}
/**
* Get the interpolation type
*/
protected int getOrder() {
return order;
}
/**
* Set the interpolation type
*/
protected void setOrder(int order) {
this.order = order;
}
/**
* Get the encoding for a particular input parameter
*
* @param i the index into the encoding array, which has size 2 * m.
* the ith entry in the array has index 2i,
* 2i + 1
* @return the encoding value if the encoding array is set, or the default
*/
protected float getEncode(int i) {
if (encode != null) {
return encode[i];
} else if ((i % 2) == 0) {
return 0f;
} else {
return (getSize(i / 2) - 1);
}
}
/**
* Set the encode array
*/
protected void setEncode(float[] encode) {
this.encode = encode;
}
/**
* Get the decoding for a particular input parameter
*
* @param i the index into the decoding array, which has size 2 * n.
* the ith entry in the array has index 2i,
* 2i + 1
* @return the decoding value if the decoding array is set, or the default
*/
protected float getDecode(int i) {
if (decode != null) {
return decode[i];
} else {
return getRange(i);
}
}
/**
* Set the decode array
*/
protected void setDecode(float[] decode) {
this.decode = decode;
}
/**
* Get a component for a sample given m indices and output
* dimension.
*
* @param values an array of m values determining which sample
* to select
* @param od the output dimension (0 - n) to get the sample in
* @return the sample for the given values and index
*/
protected int getSample(int[] values, int od) {
int mult = 1;
int index = 0;
for (int i = 0; i < values.length; i++) {
index += mult * values[i];
mult *= getSize(i);
}
return samples[index][od];
}
/**
* Set the table of samples
*/
protected void setSamples(int[][] samples) {
this.samples = samples;
}
/**
* Read the samples from the input stream. Each sample is made up
* of n components, each of which has length bitsPerSample
* bits. The samples are arranged by dimension, then range
*/
private int[][] readSamples(ByteBuffer buf) {
// calculate the number of samples in the table
int size = 1;
for (int i = 0; i < getNumInputs(); i++) {
size *= getSize(i);
}
// create the samples table
int[][] samples = new int[size][getNumOutputs()];
// the current location in the buffer, in bits from byteLoc
int bitLoc = 0;
// the current location in the buffer, in bytes
int byteLoc = 0;
// the current index in the samples array
int index = 0;
for (int i = 0; i < getNumInputs(); i++) {
for (int j = 0; j < getSize(i); j++) {
for (int k = 0; k < getNumOutputs(); k++) {
/** [JK FIXME one bit at a time is really inefficient */
int value = 0;
int toRead = getBitsPerSample();
byte curByte = buf.get(byteLoc);
while (toRead > 0) {
int nextBit = ((curByte >> (7 - bitLoc)) & 0x1);
value |= nextBit << (toRead - 1);
if (++bitLoc == 8) {
bitLoc = 0;
byteLoc++;
if (toRead > 1) {
curByte = buf.get(byteLoc);
}
}
toRead--;
}
samples[index][k] = value;
}
index++;
}
}
return samples;
}
/**
* Perform a piecewise multilinear interpolation. The provides a
* close approximation to the standard linear interpolation, at
* a far lower cost, since every element is not evaluated at every
* iteration. Instead, a walk of the most significant axes is performed,
* following the algorithm desribed at:
* http://osl.iu.edu/~tveldhui/papers/MAScThesis/node33.html
*
* @param encoded the encoded input values
* @param od the output dimension
*/
private float multilinearInterpolate(float[] encoded, int od) {
// first calculate the distances -- the differences between
// each encoded value and the integer below it.
float[] dists = new float[encoded.length];
for (int i = 0; i < dists.length; i++) {
dists[i] = (float) (encoded[i] - Math.floor(encoded[i]));
}
// initialize the map of axes. Each bit in this map represents
// whether the control value in that dimension should be the integer
// above or below encoded[i]
int map = 0;
// the initial values
float val = getSample(encoded, map, od);
float prev = val;
// walk the axes
for (int i = 0; i < dists.length; i++) {
// find the largest value of dist remaining
int idx = 0;
float largest = -1;
for (int c = 0; c < dists.length; c++) {
if (dists[c] > largest) {
largest = dists[c];
idx = c;
}
}
// now find the sample with that axis set to 1
map |= (0x1 << idx);
float cur = getSample(encoded, map, od);
// calculate the value and remember it
val += dists[idx] * (cur - prev);
prev = val;
// make sure we won't find this distance again
dists[idx] = -1;
}
// voila
return val;
}
/**
* Perform a multicubic interpolation
*
* @param encoded the encoded input values
* @param od the output dimension
*/
private float multicubicInterpolate(float[] encoded, int od) {
System.out.println("Cubic interpolation not supported!");
return multilinearInterpolate(encoded, od);
}
/**
* Perform a linear interpolation. Given a value x, and two points,
* (xmin, ymin), (xmax, ymax), where xmin <= x <= xmax, calculate a value
* y on the line from (xmin, ymin) to (xmax, ymax).
*
* @param x the x value of the input
* @param xmin the minimum x value
* @param ymin the minimum y value
* @param xmax the maximum x value
* @param ymax the maximum y value
* @return the y value interpolated from the given x
*/
public static float interpolate(float x, float xmin, float xmax,
float ymin, float ymax)
{
float value = (ymax - ymin) / (xmax - xmin);
value *= x - xmin;
value += ymin;
return value;
}
/**
* Get a sample based on an array of encoded values and a control
* map. For each bit in the map, if that bit is 0 the integer below
* the encoded value is selected, or if the bit is 1, the interger
* above is selected.
*
* @param encoded the encoded values
* @param map the bit map of control values
* @param od the output dimension to read the sample for
*/
private float getSample(float[] encoded, int map, int od) {
int[] controls = new int[encoded.length];
// fill in the controls array with appropriate ints
for (int i = 0; i < controls.length; i++) {
if ((map & (0x1 << i)) == 0) {
controls[i] = (int) Math.floor(encoded[i]);
} else {
controls[i] = (int) Math.ceil(encoded[i]);
}
}
// now return the actual sample
return getSample(controls, od);
}
}