blob: 5099fce96dd3a45fd8cd1cf2e2cd4528448c985a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.fop.render.gradient;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.fop.render.gradient.GradientMaker.DoubleFormatter;
public class Function {
public interface SubFunctionRenderer {
void outputFunction(StringBuilder out, int functionIndex);
}
/**
* Required: The Type of function (0,2,3,4) default is 0.
*/
private int functionType;
/**
* Required: 2 * m Array of Double numbers which are possible inputs to the function
*/
private List<Double> domain;
/**
* Required: 2 * n Array of Double numbers which are possible outputs to the function
*/
private List<Double> range;
/**
* Required for Type 0: Number of Bits used to represent each sample value.
* Limited to 1,2,4,8,12,16,24, or 32
*/
private int bitsPerSample = 1;
/**
* Optional for Type 0: order of interpolation between samples.
* Limited to linear (1) or cubic (3). Default is 1
*/
private int order = 1;
/**
* Optional for Type 0: A 2 * m array of Doubles which provides a
* linear mapping of input values to the domain.
*
* Required for Type 3: A 2 * k array of Doubles that, taken
* in pairs, map each subset of the domain defined by Domain
* and the Bounds array to the domain of the corresponding function.
* Should be two values per function, usually (0,1),
* as in [0 1 0 1] for 2 functions.
*/
private List<Double> encode;
/* *************************TYPE 2************************** */
/**
* Required For Type 2: An Array of n Doubles defining
* the function result when x=0. Default is [0].
*/
private float[] cZero;
/**
* Required For Type 2: An Array of n Doubles defining
* the function result when x=1. Default is [1].
*/
private float[] cOne;
/**
* Required for Type 2: The interpolation exponent.
* Each value x will return n results.
* Must be greater than 0.
*/
private double interpolationExponentN = 1;
/* *************************TYPE 3************************** */
/**
* Required for Type 3: An vector of PDFFunctions which
* form an array of k single input functions making up
* the stitching function.
*/
private List<Function> functions;
/**
* Optional for Type 3: An array of (k-1) Doubles that,
* in combination with Domain, define the intervals to which
* each function from the Functions array apply. Bounds
* elements must be in order of increasing magnitude,
* and each value must be within the value of Domain.
* k is the number of functions.
* If you pass null, it will output (1/k) in an array of k-1 elements.
* This makes each function responsible for an equal amount of the stitching function.
* It makes the gradient even.
*/
private List<Float> bounds;
/**
* create an complete Function object of Type 2, an Exponential Interpolation function.
*
* Use null for an optional object parameter if you choose not to use it.
* For optional int parameters, pass the default.
* @param domain List objects of Double objects.
* This is the domain of the function.
* See page 264 of the PDF 1.3 Spec.
* @param range List of Doubles that is the Range of the function.
* See page 264 of the PDF 1.3 Spec.
* @param cZero This is a vector of Double objects which defines the function result
* when x=0.
*
* This attribute is optional.
* It's described on page 268 of the PDF 1.3 spec.
* @param cOne This is a vector of Double objects which defines the function result
* when x=1.
*
* This attribute is optional.
* It's described on page 268 of the PDF 1.3 spec.
* @param interpolationExponentN This is the inerpolation exponent.
*
* This attribute is required.
* PDF Spec page 268
*/
public Function(List<Double> domain, List<Double> range, float[] cZero, float[] cOne,
double interpolationExponentN) {
this(2, domain, range);
this.cZero = cZero;
this.cOne = cOne;
this.interpolationExponentN = interpolationExponentN;
}
/**
* create an complete Function object of Type 3, a Stitching function.
*
* Use null for an optional object parameter if you choose not to use it.
* For optional int parameters, pass the default.
* @param domain List objects of Double objects.
* This is the domain of the function.
* See page 264 of the PDF 1.3 Spec.
* @param range List objects of Double objects.
* This is the Range of the function.
* See page 264 of the PDF 1.3 Spec.
* @param functions A List of the PDFFunction objects that the stitching function stitches.
*
* This attributed is required.
* It is described on page 269 of the PDF spec.
* @param bounds This is a vector of Doubles representing the numbers that,
* in conjunction with Domain define the intervals to which each function from
* the 'functions' object applies. It must be in order of increasing magnitude,
* and each must be within Domain.
*
* It basically sets how much of the gradient each function handles.
*
* This attributed is required.
* It's described on page 269 of the PDF 1.3 spec.
* @param encode List objects of Double objects.
* This is the linear mapping of input values intop the domain
* of the function's sample table. Default is hard to represent in
* ascii, but basically [0 (Size0 1) 0 (Size1 1)...].
* This attribute is required.
*
* See page 270 in the PDF 1.3 spec.
*/
public Function(List<Double> domain, List<Double> range, List<Function> functions,
List<Float> bounds, List<Double> encode) {
this(3, domain, range);
this.functions = functions;
this.bounds = bounds;
this.encode = makeEncode(encode);
}
private List<Double> makeEncode(List<Double> encode) {
if (encode != null) {
return encode;
} else {
encode = new ArrayList<Double>(functions.size() * 2);
for (int i = 0; i < functions.size(); i++) {
encode.add(0.0);
encode.add(1.0);
}
return encode;
}
}
private Function(int functionType, List<Double> domain, List<Double> range) {
this.functionType = functionType;
this.domain = (domain == null) ? Arrays.asList(0.0, 1.0) : domain;
this.range = range;
}
/**
* Gets the function type
*/
public int getFunctionType() {
return functionType;
}
/**
* Gets the function bounds
*/
public List<Float> getBounds() {
return bounds;
}
/**
* The function domain
*/
public List<Double> getDomain() {
return domain;
}
/**
* Gets the function encoding
*/
public List<Double> getEncode() {
return encode;
}
/**
* Gets the sub-functions
*/
public List<Function> getFunctions() {
if (functions == null) {
return Collections.emptyList();
} else {
return functions;
}
}
/**
* Gets the bits per sample of the function
*/
public int getBitsPerSample() {
return bitsPerSample;
}
/**
* Gets the interpolation exponent of the function
*/
public double getInterpolationExponentN() {
return interpolationExponentN;
}
/**
* Gets the function order
*/
public int getOrder() {
return order;
}
/**
* Gets the function range
*/
public List<Double> getRange() {
return range;
}
/**
* Gets the function C0 value (color for gradient)
*/
public float[] getCZero() {
return cZero;
}
/**
* Gets the function C1 value (color for gradient)
*/
public float[] getCOne() {
return cOne;
}
public String output(StringBuilder out, DoubleFormatter doubleFormatter,
SubFunctionRenderer subFunctionRenderer) {
out.append("<<\n/FunctionType " + functionType + "\n");
outputDomain(out, doubleFormatter);
if (this.functionType == 0) {
outputEncode(out, doubleFormatter);
outputBitsPerSample(out);
outputOrder(out);
outputRange(out, doubleFormatter);
out.append(">>");
} else if (functionType == 2) {
outputRange(out, doubleFormatter);
outputCZero(out, doubleFormatter);
outputCOne(out, doubleFormatter);
outputInterpolationExponentN(out, doubleFormatter);
out.append(">>");
} else if (functionType == 3) {
outputRange(out, doubleFormatter);
if (!functions.isEmpty()) {
out.append("/Functions [ ");
for (int i = 0; i < functions.size(); i++) {
subFunctionRenderer.outputFunction(out, i);
out.append(' ');
}
out.append("]\n");
}
outputEncode(out, doubleFormatter);
out.append("/Bounds ");
if (bounds != null) {
GradientMaker.outputDoubles(out, doubleFormatter, bounds);
} else if (!functions.isEmpty()) {
// if there are n functions,
// there must be n-1 bounds.
// so let each function handle an equal portion
// of the whole. e.g. if there are 4, then [ 0.25 0.25 0.25 ]
int numberOfFunctions = functions.size();
String functionsFraction = doubleFormatter.formatDouble(1.0 / numberOfFunctions);
out.append("[ ");
for (int i = 0; i + 1 < numberOfFunctions; i++) {
out.append(functionsFraction);
out.append(" ");
}
out.append("]");
}
out.append("\n>>");
} else if (functionType == 4) {
outputRange(out, doubleFormatter);
out.append(">>");
}
return out.toString();
}
private void outputDomain(StringBuilder p, DoubleFormatter doubleFormatter) {
p.append("/Domain ");
GradientMaker.outputDoubles(p, doubleFormatter, domain);
p.append("\n");
}
private void outputBitsPerSample(StringBuilder out) {
out.append("/BitsPerSample " + bitsPerSample + "\n");
}
private void outputOrder(StringBuilder out) {
if (order == 1 || order == 3) {
out.append("\n/Order " + order + "\n");
}
}
private void outputRange(StringBuilder out, DoubleFormatter doubleFormatter) {
if (range != null) {
out.append("/Range ");
GradientMaker.outputDoubles(out, doubleFormatter, range);
out.append("\n");
}
}
private void outputEncode(StringBuilder out, DoubleFormatter doubleFormatter) {
out.append("/Encode ");
GradientMaker.outputDoubles(out, doubleFormatter, encode);
out.append("\n");
}
private void outputCZero(StringBuilder out, DoubleFormatter doubleFormatter) {
if (cZero != null) {
out.append("/C0 [ ");
for (float c : cZero) {
out.append(doubleFormatter.formatDouble(c));
out.append(" ");
}
out.append("]\n");
}
}
private void outputCOne(StringBuilder out, DoubleFormatter doubleFormatter) {
if (cOne != null) {
out.append("/C1 [ ");
for (float c : cOne) {
out.append(doubleFormatter.formatDouble(c));
out.append(" ");
}
out.append("]\n");
}
}
private void outputInterpolationExponentN(StringBuilder out, DoubleFormatter doubleFormatter) {
out.append("/N ");
out.append(doubleFormatter.formatDouble(interpolationExponentN));
out.append("\n");
}
}