PDTriangleBasedShadingType.java (pdfbox-2.0.23-src) | : | PDTriangleBasedShadingType.java (pdfbox-2.0.24-src) | ||
---|---|---|---|---|
skipping to change at line 18 | skipping to change at line 18 | |||
* http://www.apache.org/licenses/LICENSE-2.0 | * http://www.apache.org/licenses/LICENSE-2.0 | |||
* | * | |||
* Unless required by applicable law or agreed to in writing, software | * Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | * distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
package org.apache.pdfbox.pdmodel.graphics.shading; | package org.apache.pdfbox.pdmodel.graphics.shading; | |||
import java.awt.geom.AffineTransform; | ||||
import java.awt.geom.Point2D; | ||||
import java.awt.geom.Rectangle2D; | ||||
import java.io.IOException; | ||||
import java.util.List; | ||||
import javax.imageio.stream.ImageInputStream; | ||||
import org.apache.commons.logging.Log; | ||||
import org.apache.commons.logging.LogFactory; | ||||
import org.apache.pdfbox.cos.COSArray; | import org.apache.pdfbox.cos.COSArray; | |||
import org.apache.pdfbox.cos.COSDictionary; | import org.apache.pdfbox.cos.COSDictionary; | |||
import org.apache.pdfbox.cos.COSName; | import org.apache.pdfbox.cos.COSName; | |||
import org.apache.pdfbox.pdmodel.common.PDRange; | import org.apache.pdfbox.pdmodel.common.PDRange; | |||
import org.apache.pdfbox.util.Matrix; | ||||
/** | /** | |||
* Common resources for shading types 4,5,6 and 7 | * Common resources for shading types 4,5,6 and 7 | |||
*/ | */ | |||
abstract class PDTriangleBasedShadingType extends PDShading | abstract class PDTriangleBasedShadingType extends PDShading | |||
{ | { | |||
// an array of 2^n numbers specifying the linear mapping of sample values | // an array of 2^n numbers specifying the linear mapping of sample values | |||
// into the range appropriate for the function's output values. Default | // into the range appropriate for the function's output values. Default | |||
// value: same as the value of Range | // value: same as the value of Range | |||
private COSArray decode = null; | private COSArray decode = null; | |||
private static final Log LOG = LogFactory.getLog(PDTriangleBasedShadingType. | ||||
class); | ||||
private int bitsPerCoordinate = -1; | ||||
private int bitsPerColorComponent = -1; | ||||
private int numberOfColorComponents = -1; | ||||
PDTriangleBasedShadingType(COSDictionary shadingDictionary) | PDTriangleBasedShadingType(COSDictionary shadingDictionary) | |||
{ | { | |||
super(shadingDictionary); | super(shadingDictionary); | |||
} | } | |||
/** | /** | |||
* The bits per component of this shading. This will return -1 if one has | * The bits per component of this shading. This will return -1 if one has no | |||
* not been set. | t been set. | |||
* | * | |||
* @return the number of bits per component | * @return the number of bits per component | |||
*/ | */ | |||
public int getBitsPerComponent() | public int getBitsPerComponent() | |||
{ | { | |||
return getCOSObject().getInt(COSName.BITS_PER_COMPONENT, -1); | if (bitsPerColorComponent == -1) | |||
{ | ||||
bitsPerColorComponent = getCOSObject().getInt(COSName.BITS_PER_COMPO | ||||
NENT, -1); | ||||
LOG.debug("bitsPerColorComponent: " + bitsPerColorComponent); | ||||
} | ||||
return bitsPerColorComponent; | ||||
} | } | |||
/** | /** | |||
* Set the number of bits per component. | * Set the number of bits per component. | |||
* | * | |||
* @param bitsPerComponent the number of bits per component | * @param bitsPerComponent the number of bits per component | |||
*/ | */ | |||
public void setBitsPerComponent(int bitsPerComponent) | public void setBitsPerComponent(int bitsPerComponent) | |||
{ | { | |||
getCOSObject().setInt(COSName.BITS_PER_COMPONENT, bitsPerComponent); | getCOSObject().setInt(COSName.BITS_PER_COMPONENT, bitsPerComponent); | |||
bitsPerColorComponent = bitsPerComponent; | ||||
} | } | |||
/** | /** | |||
* The bits per coordinate of this shading. This will return -1 if one has | * The bits per coordinate of this shading. This will return -1 if one has | |||
* not been set. | * not been set. | |||
* | * | |||
* @return the number of bits per coordinate | * @return the number of bits per coordinate | |||
*/ | */ | |||
public int getBitsPerCoordinate() | public int getBitsPerCoordinate() | |||
{ | { | |||
return getCOSObject().getInt(COSName.BITS_PER_COORDINATE, -1); | if (bitsPerCoordinate == -1) | |||
{ | ||||
bitsPerCoordinate = getCOSObject().getInt(COSName.BITS_PER_COORDINAT | ||||
E, -1); | ||||
LOG.debug("bitsPerCoordinate: " + (Math.pow(2, bitsPerCoordinate) - | ||||
1)); | ||||
} | ||||
return bitsPerCoordinate; | ||||
} | } | |||
/** | /** | |||
* Set the number of bits per coordinate. | * Set the number of bits per coordinate. | |||
* | * | |||
* @param bitsPerComponent the number of bits per coordinate | * @param bitsPerCoordinate the number of bits per coordinate | |||
*/ | */ | |||
public void setBitsPerCoordinate(int bitsPerComponent) | public void setBitsPerCoordinate(int bitsPerCoordinate) | |||
{ | { | |||
getCOSObject().setInt(COSName.BITS_PER_COORDINATE, bitsPerComponent); | getCOSObject().setInt(COSName.BITS_PER_COORDINATE, bitsPerCoordinate); | |||
this.bitsPerCoordinate = bitsPerCoordinate; | ||||
} | ||||
/** | ||||
* The number of color components of this shading. | ||||
* | ||||
* @return number of color components of this shading | ||||
*/ | ||||
public int getNumberOfColorComponents() throws IOException | ||||
{ | ||||
if (numberOfColorComponents == -1) | ||||
{ | ||||
numberOfColorComponents = getFunction() != null ? 1 | ||||
: getColorSpace().getNumberOfComponents(); | ||||
LOG.debug("numberOfColorComponents: " + numberOfColorComponents); | ||||
} | ||||
return numberOfColorComponents; | ||||
} | } | |||
/** | /** | |||
* Returns all decode values as COSArray. | * Returns all decode values as COSArray. | |||
* | * | |||
* @return the decode array | * @return the decode array | |||
*/ | */ | |||
private COSArray getDecodeValues() | private COSArray getDecodeValues() | |||
{ | { | |||
if (decode == null) | if (decode == null) | |||
skipping to change at line 122 | skipping to change at line 166 | |||
{ | { | |||
PDRange retval = null; | PDRange retval = null; | |||
COSArray decodeValues = getDecodeValues(); | COSArray decodeValues = getDecodeValues(); | |||
if (decodeValues != null && decodeValues.size() >= paramNum * 2 + 1) | if (decodeValues != null && decodeValues.size() >= paramNum * 2 + 1) | |||
{ | { | |||
retval = new PDRange(decodeValues, paramNum); | retval = new PDRange(decodeValues, paramNum); | |||
} | } | |||
return retval; | return retval; | |||
} | } | |||
/** | ||||
* Calculate the interpolation, see p.345 pdf spec 1.7. | ||||
* | ||||
* @param src src value | ||||
* @param srcMax max src value (2^bits-1) | ||||
* @param dstMin min dst value | ||||
* @param dstMax max dst value | ||||
* @return interpolated value | ||||
*/ | ||||
protected float interpolate(float src, long srcMax, float dstMin, float dstM | ||||
ax) | ||||
{ | ||||
return dstMin + (src * (dstMax - dstMin) / srcMax); | ||||
} | ||||
/** | ||||
* Read a vertex from the bit input stream performs interpolations. | ||||
* | ||||
* @param input bit input stream | ||||
* @param maxSrcCoord max value for source coordinate (2^bits-1) | ||||
* @param maxSrcColor max value for source color (2^bits-1) | ||||
* @param rangeX dest range for X | ||||
* @param rangeY dest range for Y | ||||
* @param colRangeTab dest range array for colors | ||||
* @param matrix the pattern matrix concatenated with that of the parent con | ||||
tent stream | ||||
* @return a new vertex with the flag and the interpolated values | ||||
* @throws IOException if something went wrong | ||||
*/ | ||||
protected Vertex readVertex(ImageInputStream input, long maxSrcCoord, long m | ||||
axSrcColor, | ||||
PDRange rangeX, PDRange rangeY, PDRange[] colRan | ||||
geTab, | ||||
Matrix matrix, AffineTransform xform) throws IOE | ||||
xception | ||||
{ | ||||
float[] colorComponentTab = new float[numberOfColorComponents]; | ||||
long x = input.readBits(bitsPerCoordinate); | ||||
long y = input.readBits(bitsPerCoordinate); | ||||
float dstX = interpolate(x, maxSrcCoord, rangeX.getMin(), rangeX.getMax( | ||||
)); | ||||
float dstY = interpolate(y, maxSrcCoord, rangeY.getMin(), rangeY.getMax( | ||||
)); | ||||
LOG.debug("coord: " + String.format("[%06X,%06X] -> [%f,%f]", x, y, dstX | ||||
, dstY)); | ||||
Point2D p = matrix.transformPoint(dstX, dstY); | ||||
xform.transform(p, p); | ||||
for (int n = 0; n < numberOfColorComponents; ++n) | ||||
{ | ||||
int color = (int) input.readBits(bitsPerColorComponent); | ||||
colorComponentTab[n] = interpolate(color, maxSrcColor, colRangeTab[n | ||||
].getMin(), | ||||
colRangeTab[n].getMax()); | ||||
LOG.debug("color[" + n + "]: " + color + "/" + String.format("%02x", | ||||
color) | ||||
+ "-> color[" + n + "]: " + colorComponentTab[n]); | ||||
} | ||||
// "Each set of vertex data shall occupy a whole number of bytes. | ||||
// If the total number of bits required is not divisible by 8, the last | ||||
data byte | ||||
// for each vertex is padded at the end with extra bits, which shall be | ||||
ignored." | ||||
int bitOffset = input.getBitOffset(); | ||||
if (bitOffset != 0) | ||||
{ | ||||
input.readBits(8 - bitOffset); | ||||
} | ||||
return new Vertex(p, colorComponentTab); | ||||
} | ||||
abstract List<ShadedTriangle> collectTriangles(AffineTransform xform, Matrix | ||||
matrix) throws IOException; | ||||
@Override | ||||
public Rectangle2D getBounds(AffineTransform xform, Matrix matrix) throws IO | ||||
Exception | ||||
{ | ||||
Rectangle2D bounds = null; | ||||
for (ShadedTriangle shadedTriangle : collectTriangles(xform, matrix)) | ||||
{ | ||||
if (bounds == null) | ||||
{ | ||||
bounds = new Rectangle2D.Double(shadedTriangle.corner[0].getX(), | ||||
shadedTriangle.corner[0].getY(), 0, 0); | ||||
} | ||||
bounds.add(shadedTriangle.corner[0]); | ||||
bounds.add(shadedTriangle.corner[1]); | ||||
bounds.add(shadedTriangle.corner[2]); | ||||
} | ||||
if (bounds == null) | ||||
{ | ||||
// Speeds up files where triangles are empty, e.g. ghostscript file | ||||
690425 | ||||
return new Rectangle2D.Float(); | ||||
} | ||||
return bounds; | ||||
} | ||||
} | } | |||
End of changes. 11 change blocks. | ||||
7 lines changed or deleted | 156 lines changed or added |