/* * $Id: FunctionType3.java,v 1.3 2010-06-14 17:32:09 lujke 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 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> */ protected void parse(PDFObject obj) throws IOException { if (getNumInputs() != 1) { throw new PDFParseException("Type 3 function only accepts a " + "single input, so Domain should have just 2 elements"); } // 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]); } int k = functions.length; PDFObject domainObj = obj.getDictRef("Domain"); if (domainObj == null) { throw new PDFParseException("domain required for function type 3!"); } // 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(); if (boundsAry.length != k - 1) { throw new PDFParseException("Bounds array length " + boundsAry.length + " should be " + (k - 1) + " with functions array length " + functions.length); } bounds = new float[boundsAry.length]; for (int i = 0; i < boundsAry.length; i++) { bounds[i] = boundsAry[i].getFloatValue(); } // 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(); if (encodeAry.length != 2 * k) { throw new PDFParseException("There should be " + (2 * k) + " values in Encode for the given number of functions."); } encode = new float[encodeAry.length]; for (int i = 0; i < encodeAry.length; i++) { encode[i] = encodeAry[i].getFloatValue(); } } /** * Map from <i>m</i> input values to <i>n</i> output values. * The number of inputs <i>m</i> 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 <i>m</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 */ protected void doFunction(float[] inputs, int inputOffset, float[] outputs, int outputOffset) { // calculate the encoded values for each input float input = inputs[inputOffset]; int subdomain = 0; while (subdomain < bounds.length && input >= bounds[subdomain]) { ++subdomain; } final float boundMin = subdomain == 0 ? getDomain(0) : bounds[subdomain - 1]; final float boundMax = subdomain == bounds.length ? getDomain(1) : bounds[subdomain]; final float encodedInput = FunctionType0.interpolate( input, boundMin, boundMax, encode[subdomain * 2], encode[subdomain * 2 + 1]); final float[] subfuncInputArr = new float[] { encodedInput }; functions[subdomain].calculate(subfuncInputArr, 0, outputs, outputOffset); } }