ParameterBlock paramBlock,
int opMask,
RenderedImage lowRes) {
// Cache RenderContext components.
AffineTransform at = renderContext.getTransform();
RenderingHints hints = renderContext.getRenderingHints();
ImagingListener listener = ImageUtil.getImagingListener(renderContext);
// Obtain the number of levels and the size of the largest one.
int[] maxSize = (int[])lowRes.getProperty("max-size");
int maxWidth = maxSize[0];
int maxHeight = maxSize[1];
int numLevels =
((Integer)lowRes.getProperty("resolution-number")).intValue();
// Calculate the aspect ratios.
float aspectRatioSource = (float)maxWidth/(float)maxHeight;
float aspectRatio = (opMask & MASK_ASPECT_RATIO) != 0 ?
paramBlock.getFloatParameter(7) : aspectRatioSource;
// Determine the bounds of the destination image.
Rectangle2D bounds2D = new Rectangle2D.Float(0.0F, 0.0F,
aspectRatio, 1.0F);
// Determine the dimensions of the rendered destination image.
int width;
int height;
if(at.isIdentity()) { // Default rendering.
AffineTransform afn =
(AffineTransform)paramBlock.getObjectParameter(6);
Rectangle2D bounds =
afn.createTransformedShape(bounds2D).getBounds2D();
double H = maxHeight*bounds.getHeight();
double W = maxHeight*bounds.getWidth();
double m = Math.max(H, W/aspectRatioSource);
height = (int)(m + 0.5);
width = (int)(aspectRatioSource*m + 0.5);
at = AffineTransform.getScaleInstance(width, height);
renderContext = (RenderContext)renderContext.clone();
renderContext.setTransform(at);
} else {
Rectangle bounds = at.createTransformedShape(bounds2D).getBounds();
width = bounds.width;
height = bounds.height;
}
// Determine which resolution level of the IIP image to request.
int res = numLevels - 1;
int hRes = maxHeight;
while(res > 0) {
hRes = (int)((hRes + 1.0F)/2.0F); // get the next height
if(hRes < height) { // stop if the next height is too small
break;
}
res--;
}
// Create a RenderableImage from the selected resolution level.
int[] subImageArray = (int[])paramBlock.getObjectParameter(1);
int subImage = subImageArray.length < res + 1 ? 0 : subImageArray[res];
if(subImage < 0) {
subImage = 0;
}
ParameterBlock pb = new ParameterBlock();
pb.add(paramBlock.getObjectParameter(0)).add(res).add(subImage);
RenderedImage iipRes = JAI.create("iipresolution", pb);
Vector sources = new Vector(1);
sources.add(iipRes);
RenderableImage ri =
new MultiResolutionRenderableImage(sources, 0.0F, 0.0F,
1.0F);
// Filtering.
if((opMask & MASK_FILTER) != 0) {
float filter = paramBlock.getFloatParameter(2);
pb = (new ParameterBlock()).addSource(ri).add(filter);
ri = new RenderableImageOp(new FilterCRIF(), pb);
}
// Color-twist.
// Cache the original number of bands in case the number of bands
// changes due to addition of chroma and/or alpha channels in the
// color-twist procedure.
int nBands = iipRes.getSampleModel().getNumBands();
if((opMask & MASK_COLOR_TWIST) != 0) {
double[][] ctw = getColorTwistMatrix(iipRes.getColorModel(),
paramBlock);
pb = (new ParameterBlock()).addSource(ri).add(ctw);
ri = JAI.createRenderable("bandcombine", pb);
nBands = ctw.length;
}
// Contrast.
if((opMask & MASK_CONTRAST) != 0) {
int csType = iipRes.getColorModel().getColorSpace().getType();
boolean isPYCC =
csType != ColorSpace.TYPE_GRAY &&
csType != ColorSpace.TYPE_RGB;
if(isPYCC) {
double[][] matrix;
if(nBands == 3) { // PYCC
matrix = composeMatrices(YCC_TO_RGB, YCC_TO_RGB_CONST);
} else { // PYCC-A
matrix = composeMatrices(YCCA_TO_RGBA, YCCA_TO_RGBA_CONST);
}
pb = (new ParameterBlock()).addSource(ri).add(matrix);
ri = JAI.createRenderable("bandcombine", pb);
}
float contrast = paramBlock.getFloatParameter(4);
LookupTableJAI lut = createContrastLUT(contrast, nBands);
pb = (new ParameterBlock()).addSource(ri).add(lut);
ri = JAI.createRenderable("lookup", pb);
if(isPYCC) {
double[][] matrix;
if(nBands == 3) { // PYCC
matrix = composeMatrices(RGB_TO_YCC, RGB_TO_YCC_CONST);
} else { // PYCC-A
matrix = composeMatrices(RGBA_TO_YCCA, RGBA_TO_YCCA_CONST);
}
pb = (new ParameterBlock()).addSource(ri).add(matrix);
ri = JAI.createRenderable("bandcombine", pb);
}
}
// Source rectangle of interest.
if((opMask & MASK_ROI_SOURCE) != 0) {
// Get the source rectangle of interest.
Rectangle2D rect = (Rectangle2D)paramBlock.getObjectParameter(5);
// Check for intersection with source bounds.
if(!rect.intersects(0.0, 0.0, aspectRatioSource, 1.0)) {
throw new RuntimeException(JaiI18N.getString("IIPCRIF5"));
}
// Create the source rectangle.
Rectangle2D rectS = new Rectangle2D.Float(0.0F, 0.0F,
aspectRatioSource, 1.0F);
// Crop out the desired region.
if(!rect.equals(rectS)) {
// Clip to the source bounds.
rect = rect.createIntersection(rectS);
// Crop to the clipped rectangle of interest.
pb = (new ParameterBlock()).addSource(ri);
pb.add((float)rect.getMinX()).add((float)rect.getMinY());
pb.add((float)rect.getWidth()).add((float)rect.getHeight());
ri = JAI.createRenderable("crop", pb);
/* XXX
// Embed the cropped image in an image the size of the source.
pb = (new ParameterBlock()).addSource(ri);
pb.add((float)rectS.getMinX()).add((float)rectS.getMinY());
pb.add((float)rectS.getWidth()).add((float)rectS.getHeight());
ri = JAI.createRenderable("crop", pb);
*/
}
}
// Spatial orientation.
if((opMask & MASK_TRANSFORM) != 0) {
AffineTransform afn =
(AffineTransform)paramBlock.getObjectParameter(6);
try {
// The transform parameter is a backward mapping so invert it.
afn = afn.createInverse();
} catch(java.awt.geom.NoninvertibleTransformException e) {
// This should never happen due to descriptor check.
listener.errorOccurred(JaiI18N.getString("AffineNotInvertible"),
e, this, false);
}
pb = (new ParameterBlock()).addSource(ri).add(afn);
if(hints != null && hints.containsKey(JAI.KEY_INTERPOLATION)) {
pb.add(hints.get(JAI.KEY_INTERPOLATION));
}
ri = JAI.createRenderable("affine", pb);
}
// Destination rectangle of interest.