org.apache.pdfbox.pdmodel.graphics.shading.TensorPatch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pdfbox Show documentation
Show all versions of pdfbox Show documentation
The Apache PDFBox library is an open source Java tool for working with PDF documents.
/*
* Copyright 2014 The Apache Software Foundation.
*
* 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.apache.pdfbox.pdmodel.graphics.shading;
import java.awt.geom.Point2D;
import java.util.List;
/**
* This class is used to describe a patch for type 7 shading. This was done as
* part of GSoC2014, Tilman Hausherr is the mentor.
*
* @author Shaola Ren
*/
class TensorPatch extends Patch
{
/**
* Constructor of a patch for type 7 shading.
*
* @param tcp 16 control points
* @param color 4 corner colors
*/
protected TensorPatch(Point2D[] tcp, float[][] color)
{
super(tcp, color);
controlPoints = reshapeControlPoints(tcp);
level = calcLevel();
listOfTriangles = getTriangles();
}
/*
order the 16 1d points to a square matrix which is as the one described
in p.199 of PDF3200_2008.pdf rotated 90 degrees clockwise
*/
private Point2D[][] reshapeControlPoints(Point2D[] tcp)
{
Point2D[][] square = new Point2D[4][4];
for (int i = 0; i <= 3; i++)
{
square[0][i] = tcp[i];
square[3][i] = tcp[9 - i];
}
for (int i = 1; i <= 2; i++)
{
square[i][0] = tcp[12 - i];
square[i][2] = tcp[12 + i];
square[i][3] = tcp[3 + i];
}
square[1][1] = tcp[12];
square[2][1] = tcp[15];
return square;
}
// calculate the dividing level from the control points
private int[] calcLevel()
{
int[] l =
{
4, 4
};
Point2D[] ctlC1 = new Point2D[4];
Point2D[] ctlC2 = new Point2D[4];
for (int j = 0; j < 4; j++)
{
ctlC1[j] = controlPoints[j][0];
ctlC2[j] = controlPoints[j][3];
}
// if two opposite edges are both lines, there is a possibility to reduce the dividing level
if (isEdgeALine(ctlC1) && isEdgeALine(ctlC2))
{
/*
if any of the 4 inner control points is out of the patch formed by the 4 edges,
keep the high dividing level,
otherwise, determine the dividing level by the lengths of edges
*/
if (isOnSameSideCC(controlPoints[1][1]) || isOnSameSideCC(controlPoints[1][2])
|| isOnSameSideCC(controlPoints[2][1]) || isOnSameSideCC(controlPoints[2][2]))
{
// keep the high dividing level
}
else
{
// length's unit is one pixel in device space
double lc1 = getLen(ctlC1[0], ctlC1[3]), lc2 = getLen(ctlC2[0], ctlC2[3]);
if (lc1 > 800 || lc2 > 800)
{
// keeps init value 4
}
else if (lc1 > 400 || lc2 > 400)
{
l[0] = 3;
}
else if (lc1 > 200 || lc2 > 200)
{
l[0] = 2;
}
else
{
l[0] = 1;
}
}
}
// the other two opposite edges
if (isEdgeALine(controlPoints[0]) && isEdgeALine(controlPoints[3]))
{
if (isOnSameSideDD(controlPoints[1][1]) || isOnSameSideDD(controlPoints[1][2])
|| isOnSameSideDD(controlPoints[2][1]) || isOnSameSideDD(controlPoints[2][2]))
{
// keep the high dividing level
}
else
{
double ld1 = getLen(controlPoints[0][0], controlPoints[0][3]);
double ld2 = getLen(controlPoints[3][0], controlPoints[3][3]);
if (ld1 > 800 || ld2 > 800)
{
// keeps init value 4
}
else if (ld1 > 400 || ld2 > 400)
{
l[1] = 3;
}
else if (ld1 > 200 || ld2 > 200)
{
l[1] = 2;
}
else
{
l[1] = 1;
}
}
}
return l;
}
// whether a point is on the same side of edge C1 and edge C2
private boolean isOnSameSideCC(Point2D p)
{
double cc = edgeEquationValue(p, controlPoints[0][0], controlPoints[3][0])
* edgeEquationValue(p, controlPoints[0][3], controlPoints[3][3]);
return cc > 0;
}
// whether a point is on the same side of edge D1 and edge D2
private boolean isOnSameSideDD(Point2D p)
{
double dd = edgeEquationValue(p, controlPoints[0][0], controlPoints[0][3])
* edgeEquationValue(p, controlPoints[3][0], controlPoints[3][3]);
return dd > 0;
}
// get a list of triangles which compose this tensor patch
private List getTriangles()
{
CoordinateColorPair[][] patchCC = getPatchCoordinatesColor();
return getShadedTriangles(patchCC);
}
@Override
protected Point2D[] getFlag1Edge()
{
Point2D[] implicitEdge = new Point2D[4];
for (int i = 0; i < 4; i++)
{
implicitEdge[i] = controlPoints[i][3];
}
return implicitEdge;
}
@Override
protected Point2D[] getFlag2Edge()
{
Point2D[] implicitEdge = new Point2D[4];
for (int i = 0; i < 4; i++)
{
implicitEdge[i] = controlPoints[3][3 - i];
}
return implicitEdge;
}
@Override
protected Point2D[] getFlag3Edge()
{
Point2D[] implicitEdge = new Point2D[4];
for (int i = 0; i < 4; i++)
{
implicitEdge[i] = controlPoints[3 - i][0];
}
return implicitEdge;
}
/*
dividing a patch into a grid according to level, then calculate the coordinate and color of
each crossing point in the grid, the rule to calculate the coordinate is tensor-product which
is defined in page 119 of PDF32000_2008.pdf, the method to calculate the cooresponding color is
bilinear interpolation
*/
private CoordinateColorPair[][] getPatchCoordinatesColor()
{
int numberOfColorComponents = cornerColor[0].length;
double[][] bernsteinPolyU = getBernsteinPolynomials(level[0]);
int szU = bernsteinPolyU[0].length;
double[][] bernsteinPolyV = getBernsteinPolynomials(level[1]);
int szV = bernsteinPolyV[0].length;
CoordinateColorPair[][] patchCC = new CoordinateColorPair[szV][szU];
double stepU = 1.0 / (szU - 1);
double stepV = 1.0 / (szV - 1);
double v = -stepV;
for (int k = 0; k < szV; k++)
{
// v and u are the assistant parameters
v += stepV;
double u = -stepU;
for (int l = 0; l < szU; l++)
{
double tmpx = 0.0;
double tmpy = 0.0;
// these two "for" loops are for the equation to define the patch surface (coordinates)
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
tmpx += controlPoints[i][j].getX() * bernsteinPolyU[i][l] * bernsteinPolyV[j][k];
tmpy += controlPoints[i][j].getY() * bernsteinPolyU[i][l] * bernsteinPolyV[j][k];
}
}
Point2D tmpC = new Point2D.Double(tmpx, tmpy);
u += stepU;
float[] paramSC = new float[numberOfColorComponents];
for (int ci = 0; ci < numberOfColorComponents; ci++)
{
paramSC[ci] = (float) ((1 - v) * ((1 - u) * cornerColor[0][ci] + u * cornerColor[3][ci])
+ v * ((1 - u) * cornerColor[1][ci] + u * cornerColor[2][ci])); // bilinear interpolation
}
patchCC[k][l] = new CoordinateColorPair(tmpC, paramSC);
}
}
return patchCC;
}
// Bernstein polynomials which are defined in page 119 of PDF32000_2008.pdf
private double[][] getBernsteinPolynomials(int lvl)
{
int sz = (1 << lvl) + 1;
double[][] poly = new double[4][sz];
double step = 1.0 / (sz - 1);
double t = -step;
for (int i = 0; i < sz; i++)
{
t += step;
poly[0][i] = (1 - t) * (1 - t) * (1 - t);
poly[1][i] = 3 * t * (1 - t) * (1 - t);
poly[2][i] = 3 * t * t * (1 - t);
poly[3][i] = t * t * t;
}
return poly;
}
}