/*
* 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 com.sun.pdfview.PDFObject;
import com.sun.pdfview.PDFParseException;
/**
* 3.9.3 - A stitching function define a <i>stitching</i> of the subdomains of
* several 1-input functions to produce a single new 1-input function.
* Since the resulting stitching function is a 1-input function, the
* domain is given by a two-element array, [ <b>Domain</b>0 <b>Domain</b>1 ].
*
* <pre>
* Example 4.25
* 5 0 obj % Shading dictionary
* << /ShadingType 3
* /ColorSpace /DeviceCMYK
* /Coords [ 0.0 0.0 0.096 0.0 0.0 1.0 00]% Concentric circles
* /Function 10 0 R
* /Extend [ true true ]
* >>
* endobj
*
* 10 0 obj % Color function
* << /FunctionType 3
* /Domain [ 0.0 1.0 ]
* /Functions [ 11 0 R 12 0 R ]
* /Bounds [ 0.708 ]
* /Encode [ 1.0 0.0 0.0 1.0 ]
* >>
* endobj
*
* 11 0 obj % First subfunction
* << /FunctionType 2
* /Domain [ 0.0 1.0 ]
* /C0 [ 0.929 0.357 1.000 0.298 ]
* /C1 [ 0.631 0.278 1.000 0.027 ]
* /N 1.048
* >>
* endobj
*
* 12 0 obj % Second subfunction
* << /FunctionType 2
* /Domain [ 0.0 1.0 ]
* /C0 [ 0.929 0.357 1.000 0.298 ]
* /C1 [ 0.941 0.400 1.000 0.102 ]
* /N 1.374
* >>
* endobj
* </pre>
*/
public class FunctionType3 extends PDFFunction {
private PDFFunction[] functions;
private float[] bounds;
private float[] encode;
/** Creates a new instance of FunctionType3 */
protected FunctionType3() {
super(TYPE_3);
}
/**
* <p>Read the function information from a PDF Object.</p>
* <p>Required entries ( Table 3.38) (3200-1:2008:7.10.4, table: 41)
* are:<li>
*
* <b>Functions</b> <i>array</i> (Required) An array of k 1-input functions making up
* the stitching function. The output dimensionality of all functions
* must be the same, and compatible with the value of <b>Range</b>
* if <b>Range</b> is present.</li><li>
*
* <b>Domain</b><i>array</i> (Required) A 2 element array where
* <b>Domain</b>0 is less than <b>Domain</b>1. This is read by the
* <code>PDFFunction</code> superclass.</li><li>
*
* <b>Bounds</b> <i>array</i> (Required) An array of k-1 numbers that,
* in combination with <b>Domain</b>, define the intervals to which each
* function from the <b>Functions</b> array applies. <b>Bounds</b> elements
* must be in order of increasing value, and each value must be within
* the domain defined by >b>Domain</b>.</li><li>
*
* <b>Encode</b> <i>array</i> (Required) An array of 2 * k numbers that,
* taken in pairs, map each subset of the domain defined by <bDomain</b>
* and the <b>Bounds</b> array to the domain of the corresponding function.
* </li></p>
*/
@Override
protected void parse(PDFObject obj) throws IOException {
// read the Functions array (required)
PDFObject functionsObj = obj.getDictRef("Functions");
if (functionsObj == null) {
throw new PDFParseException("Functions required for function type 3!");
}
PDFObject[] functionsAry = functionsObj.getArray();
functions = new PDFFunction[functionsAry.length];
for (int i = 0; i < functionsAry.length; i++) {
functions[i] = PDFFunction.getFunction(functionsAry[i]);
}
// read the Bounds array (required)
PDFObject boundsObj = obj.getDictRef("Bounds");
if (boundsObj == null) {
throw new PDFParseException("Bounds required for function type 3!");
}
PDFObject[] boundsAry = boundsObj.getArray();
bounds = new float[boundsAry.length + 2];
if (bounds.length - 2 != functions.length - 1) {
throw new PDFParseException("Bounds array must be of length " + (functions.length - 1));
}
for (int i = 0; i < boundsAry.length; i++) {
bounds[i+1] = boundsAry[i].getFloatValue();
}
bounds[0] = getDomain(0);
bounds[bounds.length-1] = getDomain(1);
// read the encode array (required)
PDFObject encodeObj = obj.getDictRef("Encode");
if (encodeObj == null) {
throw new PDFParseException("Encode required for function type 3!");
}
PDFObject[] encodeAry = encodeObj.getArray();
encode = new float[encodeAry.length];
if (encode.length != 2*functions.length) {
throw new PDFParseException("Encode array must be of length " + 2*functions.length);
}
for (int i = 0; i < encodeAry.length; i++) {
encode[i] = encodeAry[i].getFloatValue();
}
}
/**
*
* @param inputs an array of <i>1</i> input values
* @param outputs an array of size <i>n</i> which will be filled
* with the output values, or null to return a new array
*/
@Override
protected void doFunction(float[] inputs, int inputOffset,
float[] outputs, int outputOffset) {
float x = inputs[inputOffset];
// calculate the output values
int p = bounds.length - 2;
while (x < bounds[p]) p--;
x = interpolate(x, bounds[p], bounds[p+1], encode[2*p], encode[2*p + 1]);
float[] out = functions[p].calculate(new float[]{x});
for (int i = 0; i < out.length; i++) {
outputs[i + outputOffset] = out[i];
}
}
@Override
public int getNumInputs() {
return 1;
}
@Override
public int getNumOutputs() {
return functions[0].getNumOutputs();
}
}