0001: /*
0002: * $RCSfile: ScaleOpImage.java,v $
0003: *
0004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Use is subject to license terms.
0007: *
0008: * $Revision: 1.1 $
0009: * $Date: 2005/02/11 04:57:21 $
0010: * $State: Exp $
0011: */
0012: package javax.media.jai;
0013:
0014: import com.sun.media.jai.util.Rational;
0015: import com.sun.media.jai.util.ImageUtil;
0016: import java.awt.Rectangle;
0017: import java.awt.RenderingHints;
0018: import java.awt.geom.Point2D;
0019: import java.awt.image.Raster;
0020: import java.awt.image.RenderedImage;
0021: import java.awt.image.WritableRaster;
0022: import java.awt.Point;
0023: import java.util.Map;
0024: import java.util.LinkedList;
0025: import javax.media.jai.util.CaselessStringKey;
0026:
0027: /**
0028: * A class extending <code>WarpOpImage</code> for use by further
0029: * extension classes that perform image scaling. Image scaling operations
0030: * require rectilinear backwards mapping and padding by the resampling
0031: * filter dimensions.
0032: *
0033: * <p> When applying scale factors of scaleX, scaleY to a source image
0034: * with the upper left pixel at (srcMinX, srcMinY) and width of srcWidth
0035: * and height of srcHeight, the resulting image is defined to have the
0036: * following bounds:
0037: *
0038: * <code>
0039: * dstMinX = ceil(A), where A = srcMinX * scaleX - 0.5 + transX,
0040: * dstMinY = ceil(B), where B = srcMinY * scaleY - 0.5 + transY,
0041: * dstMaxX = ceil(C), where C = (srcMaxX + 1) * scaleX - 1.5 + transX
0042: * and srcMaxX = srcMinX + srcWidth - 1
0043: * dstMaxY = ceil(D), where D = (srcMaxY + 1) * scaleY - 1.5 + transY
0044: * and srcMaxY = srcMinY + srcHeight - 1
0045: * dstWidth = dstMaxX - dstMinX + 1
0046: * dstHeight = dstMaxY - dstMinY + 1
0047: * </code>
0048: *
0049: * <p> In the case where source's upper left pixel is located is (0, 0),
0050: * the formulae simplify to
0051: *
0052: * <code>
0053: * dstMinX = 0
0054: * dstMinY = 0
0055: * dstWidth = ceil (srcWidth * scaleX - 0.5 + transX)
0056: * dstHeight = ceil (srcHeight * scaleY - 0.5 + transY)
0057: * </code>
0058: *
0059: * <p> In the case where the source's upper left pixel is located at (0, 0)
0060: * and the scaling factors are integers, the formulae further simplify to
0061: *
0062: * <code>
0063: * dstMinX = 0
0064: * dstMinY = 0
0065: * dstWidth = ceil (srcWidth * scaleX + transX)
0066: * dstWidth = ceil (srcHeight * scaleY + transY)
0067: * </code>
0068: *
0069: * <p> When interpolations which require padding the source such as Bilinear
0070: * or Bicubic interpolation are specified, the source needs to be extended
0071: * such that it has the extra pixels needed to compute all the destination
0072: * pixels. This extension is performed via the <code>BorderExtender</code>
0073: * class. The type of border extension can be specified as a
0074: * <code>RenderingHint</code> to the <code>JAI.create</code> method.
0075: *
0076: * <p> If no <code>BorderExtender</code> is specified, the source will
0077: * not be extended. The scaled image size is still calculated
0078: * according to the formula specified above. However since there is not
0079: * enough source to compute all the destination pixels, only that
0080: * subset of the destination image's pixels which can be computed,
0081: * will be written in the destination. The rest of the destination
0082: * will be set to zeros.
0083: *
0084: * <p> It may be noted that the minX, minY, width and height hints as
0085: * specified through the <code>JAI.KEY_IMAGE_LAYOUT</code> hint in the
0086: * <code>RenderingHints</code> object are not honored, as this operator
0087: * calculates the destination image bounds itself. The other
0088: * <code>ImageLayout</code> hints, like tileWidth and tileHeight,
0089: * however are honored.
0090: *
0091: * It should be noted that the superclass <code>GeometricOpImage</code>
0092: * automatically adds a value of <code>Boolean.TRUE</code> for the
0093: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> to the given
0094: * <code>configuration</code> and passes it up to its superclass constructor
0095: * so that geometric operations are performed on the pixel values instead
0096: * of being performed on the indices into the color map for those
0097: * operations whose source(s) have an <code>IndexColorModel</code>.
0098: * This addition will take place only if a value for the
0099: * <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> has not already been
0100: * provided by the user. Note that the <code>configuration</code> Map
0101: * is cloned before the new hint is added to it. Regarding the value for
0102: * the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code>
0103: * <code>RenderingHints</code>, the operator itself can be smart
0104: * based on the parameters, i.e. while the default value for
0105: * the <code>JAI.KEY_REPLACE_INDEX_COLOR_MODEL</code> is
0106: * <code>Boolean.TRUE</code> for operations that extend this class,
0107: * in some cases the operator could set the default.
0108: *
0109: * @see WarpOpImage
0110: * @see OpImage
0111: *
0112: */
0113: public abstract class ScaleOpImage extends GeometricOpImage {
0114:
0115: /** The horizontal scale factor. */
0116: protected float scaleX;
0117:
0118: /** The vertical scale factor. */
0119: protected float scaleY;
0120:
0121: /** Thee horizontal translation factor */
0122: protected float transX;
0123:
0124: /** The vertical translation factor */
0125: protected float transY;
0126:
0127: /*** Rational representations */
0128: protected Rational scaleXRational, scaleYRational;
0129: protected long scaleXRationalNum, scaleXRationalDenom;
0130: protected long scaleYRationalNum, scaleYRationalDenom;
0131:
0132: protected Rational invScaleXRational, invScaleYRational;
0133: protected long invScaleXRationalNum, invScaleXRationalDenom;
0134: protected long invScaleYRationalNum, invScaleYRationalDenom;
0135:
0136: protected Rational transXRational, transYRational;
0137: protected long transXRationalNum, transXRationalDenom;
0138: protected long transYRationalNum, transYRationalDenom;
0139:
0140: protected static float rationalTolerance = 0.000001F;
0141:
0142: // Padding
0143: private int lpad, rpad, tpad, bpad;
0144:
0145: // FORMULAE FOR FORWARD MAP are derived as follows
0146: // Nearest
0147: // Minimum:
0148: // srcMin = floor ((dstMin + 0.5 - trans) / scale)
0149: // srcMin <= (dstMin + 0.5 - trans) / scale < srcMin + 1
0150: // srcMin*scale <= dstMin + 0.5 - trans < (srcMin + 1)*scale
0151: // srcMin*scale - 0.5 + trans
0152: // <= dstMin < (srcMin + 1)*scale - 0.5 + trans
0153: // Let A = srcMin*scale - 0.5 + trans,
0154: // Let B = (srcMin + 1)*scale - 0.5 + trans
0155: //
0156: // dstMin = ceil(A)
0157: //
0158: // Maximum:
0159: // Note that srcMax is defined to be srcMin + dimension - 1
0160: // srcMax = floor ((dstMax + 0.5 - trans) / scale)
0161: // srcMax <= (dstMax + 0.5 - trans) / scale < srcMax + 1
0162: // srcMax*scale <= dstMax + 0.5 - trans < (srcMax + 1)*scale
0163: // srcMax*scale - 0.5 + trans
0164: // <= dstMax < (srcMax+1) * scale - 0.5 + trans
0165: // Let float A = (srcMax + 1) * scale - 0.5 + trans
0166: //
0167: // dstMax = floor(A), if floor(A) < A, else
0168: // dstMax = floor(A) - 1
0169: // OR dstMax = ceil(A - 1)
0170: //
0171: // Other interpolations
0172: //
0173: // First the source should be shrunk by the padding that is
0174: // required for the particular interpolation. Then the
0175: // shrunk source should be forward mapped as follows:
0176: //
0177: // Minimum:
0178: // srcMin = floor (((dstMin + 0.5 - trans)/scale) - 0.5)
0179: // srcMin <= ((dstMin + 0.5 - trans)/scale) - 0.5 < srcMin+1
0180: // (srcMin+0.5)*scale <= dstMin+0.5-trans <
0181: // (srcMin+1.5)*scale
0182: // (srcMin+0.5)*scale - 0.5 + trans
0183: // <= dstMin < (srcMin+1.5)*scale - 0.5 + trans
0184: // Let A = (srcMin+0.5)*scale - 0.5 + trans,
0185: // Let B = (srcMin+1.5)*scale - 0.5 + trans
0186: //
0187: // dstMin = ceil(A)
0188: //
0189: // Maximum:
0190: // srcMax is defined as srcMin + dimension - 1
0191: // srcMax = floor (((dstMax + 0.5 - trans) / scale) - 0.5)
0192: // srcMax <= ((dstMax + 0.5 - trans)/scale) - 0.5 < srcMax+1
0193: // (srcMax+0.5)*scale <= dstMax + 0.5 - trans <
0194: // (srcMax+1.5)*scale
0195: // (srcMax+0.5)*scale - 0.5 + trans
0196: // <= dstMax < (srcMax+1.5)*scale - 0.5 + trans
0197: // Let float A = (srcMax+1.5)*scale - 0.5 + trans
0198: //
0199: // dstMax = floor(A), if floor(A) < A, else
0200: // dstMax = floor(A) - 1
0201: // OR dstMax = ceil(A - 1)
0202: //
0203:
0204: private static ImageLayout layoutHelper(RenderedImage source,
0205: float scaleX, float scaleY, float transX, float transY,
0206: Interpolation interp, ImageLayout il) {
0207:
0208: // Represent the scale factors as Rational numbers.
0209: // Since a value of 1.2 is represented as 1.200001 which
0210: // throws the forward/backward mapping in certain situations.
0211: // Convert the scale and translation factors to Rational numbers
0212: Rational scaleXRational = Rational.approximate(scaleX,
0213: rationalTolerance);
0214:
0215: Rational scaleYRational = Rational.approximate(scaleY,
0216: rationalTolerance);
0217:
0218: long scaleXRationalNum = (long) scaleXRational.num;
0219: long scaleXRationalDenom = (long) scaleXRational.denom;
0220: long scaleYRationalNum = (long) scaleYRational.num;
0221: long scaleYRationalDenom = (long) scaleYRational.denom;
0222:
0223: Rational transXRational = Rational.approximate(transX,
0224: rationalTolerance);
0225:
0226: Rational transYRational = Rational.approximate(transY,
0227: rationalTolerance);
0228:
0229: long transXRationalNum = (long) transXRational.num;
0230: long transXRationalDenom = (long) transXRational.denom;
0231: long transYRationalNum = (long) transYRational.num;
0232: long transYRationalDenom = (long) transYRational.denom;
0233:
0234: ImageLayout layout = (il == null) ? new ImageLayout()
0235: : (ImageLayout) il.clone();
0236:
0237: int x0 = source.getMinX();
0238: int y0 = source.getMinY();
0239: int w = source.getWidth();
0240: int h = source.getHeight();
0241:
0242: // Variables to store the calculated destination upper left coordinate
0243: long dx0Num, dx0Denom, dy0Num, dy0Denom;
0244:
0245: // Variables to store the calculated destination bottom right
0246: // coordinate
0247: long dx1Num, dx1Denom, dy1Num, dy1Denom;
0248:
0249: // Start calculations for destination
0250:
0251: dx0Num = x0;
0252: dx0Denom = 1;
0253:
0254: dy0Num = y0;
0255: dy0Denom = 1;
0256:
0257: // Formula requires srcMaxX + 1 = (x0 + w - 1) + 1 = x0 + w
0258: dx1Num = x0 + w;
0259: dx1Denom = 1;
0260:
0261: // Formula requires srcMaxY + 1 = (y0 + h - 1) + 1 = y0 + h
0262: dy1Num = y0 + h;
0263: dy1Denom = 1;
0264:
0265: dx0Num *= scaleXRationalNum;
0266: dx0Denom *= scaleXRationalDenom;
0267:
0268: dy0Num *= scaleYRationalNum;
0269: dy0Denom *= scaleYRationalDenom;
0270:
0271: dx1Num *= scaleXRationalNum;
0272: dx1Denom *= scaleXRationalDenom;
0273:
0274: dy1Num *= scaleYRationalNum;
0275: dy1Denom *= scaleYRationalDenom;
0276:
0277: // Equivalent to subtracting 0.5
0278: dx0Num = 2 * dx0Num - dx0Denom;
0279: dx0Denom *= 2;
0280:
0281: dy0Num = 2 * dy0Num - dy0Denom;
0282: dy0Denom *= 2;
0283:
0284: // Equivalent to subtracting 1.5
0285: dx1Num = 2 * dx1Num - 3 * dx1Denom;
0286: dx1Denom *= 2;
0287:
0288: dy1Num = 2 * dy1Num - 3 * dy1Denom;
0289: dy1Denom *= 2;
0290:
0291: // Adding translation factors
0292:
0293: // Equivalent to float dx0 += transX
0294: dx0Num = dx0Num * transXRationalDenom + transXRationalNum
0295: * dx0Denom;
0296: dx0Denom *= transXRationalDenom;
0297:
0298: // Equivalent to float dy0 += transY
0299: dy0Num = dy0Num * transYRationalDenom + transYRationalNum
0300: * dy0Denom;
0301: dy0Denom *= transYRationalDenom;
0302:
0303: // Equivalent to float dx1 += transX
0304: dx1Num = dx1Num * transXRationalDenom + transXRationalNum
0305: * dx1Denom;
0306: dx1Denom *= transXRationalDenom;
0307:
0308: // Equivalent to float dy1 += transY
0309: dy1Num = dy1Num * transYRationalDenom + transYRationalNum
0310: * dy1Denom;
0311: dy1Denom *= transYRationalDenom;
0312:
0313: // Get the integral coordinates
0314: int l_x0, l_y0, l_x1, l_y1;
0315:
0316: l_x0 = Rational.ceil(dx0Num, dx0Denom);
0317: l_y0 = Rational.ceil(dy0Num, dy0Denom);
0318:
0319: l_x1 = Rational.ceil(dx1Num, dx1Denom);
0320: l_y1 = Rational.ceil(dy1Num, dy1Denom);
0321:
0322: // Set the top left coordinate of the destination
0323: layout.setMinX(l_x0);
0324: layout.setMinY(l_y0);
0325:
0326: // Width and height
0327: layout.setWidth(l_x1 - l_x0 + 1);
0328: layout.setHeight(l_y1 - l_y0 + 1);
0329:
0330: return layout;
0331: }
0332:
0333: private static Map configHelper(RenderedImage source,
0334: Map configuration, Interpolation interp) {
0335:
0336: Map config = configuration;
0337:
0338: // If source image is binary and the interpolation is either nearest
0339: // or bilinear, do not expand
0340: if (ImageUtil.isBinary(source.getSampleModel())
0341: && (interp == null
0342: || interp instanceof InterpolationNearest || interp instanceof InterpolationBilinear)) {
0343:
0344: // Set to false
0345: if (configuration == null) {
0346: config = new RenderingHints(
0347: JAI.KEY_REPLACE_INDEX_COLOR_MODEL,
0348: Boolean.FALSE);
0349: } else {
0350:
0351: // If the user specified a value for this hint, we don't
0352: // want to change that
0353: if (!config
0354: .containsKey(JAI.KEY_REPLACE_INDEX_COLOR_MODEL)) {
0355: RenderingHints hints = new RenderingHints(null);
0356: // This is effectively a clone of configuration
0357: hints.putAll(configuration);
0358: config = hints;
0359: config.put(JAI.KEY_REPLACE_INDEX_COLOR_MODEL,
0360: Boolean.TRUE);
0361: }
0362: }
0363: }
0364:
0365: return config;
0366: }
0367:
0368: /**
0369: * Constructs a <code>ScaleOpImage</code> from a <code>RenderedImage</code>
0370: * source, an optional <code>BorderExtender</code>, x and y scale
0371: * and translation factors, and an <code>Interpolation</code>
0372: * object. The image dimensions are determined by forward-mapping
0373: * the source bounds, and are passed to the superclass constructor
0374: * by means of the <code>layout</code> parameter. Other fields of
0375: * the layout are passed through unchanged. If
0376: * <code>layout</code> is <code>null</code>, a new
0377: * <code>ImageLayout</code> will be constructor to hold the bounds
0378: * information.
0379: *
0380: * Note that the scale factors are represented internally as Rational
0381: * numbers in order to workaround inexact device specific representation
0382: * of floating point numbers. For instance the floating point number 1.2
0383: * is internally represented as 1.200001, which can throw the
0384: * calculations off during a forward/backward map.
0385: *
0386: * <p> The Rational approximation is valid upto the sixth decimal place.
0387: *
0388: * @param layout an <code>ImageLayout</code> optionally containing
0389: * the tile grid layout, <code>SampleModel</code>, and
0390: * <code>ColorModel</code>, or <code>null</code>.
0391: * @param source a <code>RenderedImage</code>.
0392: * @param configuration Configurable attributes of the image including
0393: * configuration variables indexed by
0394: * <code>RenderingHints.Key</code>s and image properties indexed
0395: * by <code>String</code>s or <code>CaselessStringKey</code>s.
0396: * This is simply forwarded to the superclass constructor.
0397: * @param cobbleSources a boolean indicating whether
0398: * <code>computeRect</code> expects contiguous sources.
0399: * @param extender a <code>BorderExtender</code>, or <code>null</code>.
0400: * @param interp an <code>Interpolation</code> object to use for
0401: * resampling.
0402: * @param scaleX scale factor along x axis.
0403: * @param scaleY scale factor along y axis.
0404: * @param transX translation factor along x axis.
0405: * @param transY translation factor along y axis.
0406: *
0407: * @throws IllegalArgumentException if <code>source</code>
0408: * is <code>null</code>.
0409: * @throws IllegalArgumentException if combining the
0410: * source bounds with the layout parameter results in negative
0411: * output width or height.
0412: *
0413: * @since JAI 1.1
0414: */
0415: public ScaleOpImage(RenderedImage source, ImageLayout layout,
0416: Map configuration, boolean cobbleSources,
0417: BorderExtender extender, Interpolation interp,
0418: float scaleX, float scaleY, float transX, float transY) {
0419: super (
0420: vectorize(source), // vectorize() checks for null source.
0421: layoutHelper(source, scaleX, scaleY, transX, transY,
0422: interp, layout), configHelper(source,
0423: configuration, interp), cobbleSources,
0424: extender, interp, null);
0425:
0426: this .scaleX = scaleX;
0427: this .scaleY = scaleY;
0428: this .transX = transX;
0429: this .transY = transY;
0430:
0431: // Represent the scale factors as Rational numbers.
0432: // Since a value of 1.2 is represented as 1.200001 which
0433: // throws the forward/backward mapping in certain situations.
0434: // Convert the scale and translation factors to Rational numbers
0435: this .scaleXRational = Rational.approximate(scaleX,
0436: rationalTolerance);
0437: this .scaleYRational = Rational.approximate(scaleY,
0438: rationalTolerance);
0439:
0440: this .scaleXRationalNum = (long) this .scaleXRational.num;
0441: this .scaleXRationalDenom = (long) this .scaleXRational.denom;
0442: this .scaleYRationalNum = (long) this .scaleYRational.num;
0443: this .scaleYRationalDenom = (long) this .scaleYRational.denom;
0444:
0445: this .transXRational = Rational.approximate(transX,
0446: rationalTolerance);
0447: this .transYRational = Rational.approximate(transY,
0448: rationalTolerance);
0449:
0450: this .transXRationalNum = (long) this .transXRational.num;
0451: this .transXRationalDenom = (long) this .transXRational.denom;
0452: this .transYRationalNum = (long) this .transYRational.num;
0453: this .transYRationalDenom = (long) this .transYRational.denom;
0454:
0455: // Inverse scale factors as Rationals
0456: invScaleXRational = new Rational(scaleXRational);
0457: invScaleXRational.invert();
0458: invScaleYRational = new Rational(scaleYRational);
0459: invScaleYRational.invert();
0460: invScaleXRationalNum = invScaleXRational.num;
0461: invScaleXRationalDenom = invScaleXRational.denom;
0462: invScaleYRationalNum = invScaleYRational.num;
0463: invScaleYRationalDenom = invScaleYRational.denom;
0464:
0465: lpad = interp.getLeftPadding();
0466: rpad = interp.getRightPadding();
0467: tpad = interp.getTopPadding();
0468: bpad = interp.getBottomPadding();
0469:
0470: if (extender == null) {
0471:
0472: // Get the source dimensions
0473: int x0 = source.getMinX();
0474: int y0 = source.getMinY();
0475: int w = source.getWidth();
0476: int h = source.getHeight();
0477:
0478: // The first source pixel (x0, y0)
0479: long dx0Num, dx0Denom, dy0Num, dy0Denom;
0480:
0481: // The first pixel (x1, y1) that is just outside the source
0482: long dx1Num, dx1Denom, dy1Num, dy1Denom;
0483:
0484: if (interp instanceof InterpolationNearest) {
0485:
0486: // First point inside the source
0487: dx0Num = x0;
0488: dx0Denom = 1;
0489:
0490: dy0Num = y0;
0491: dy0Denom = 1;
0492:
0493: // First point outside source
0494: // for nearest, x1 = x0 + w, y1 = y0 + h
0495: // since anything >= a but < (a+1) maps to a for nearest
0496:
0497: // Equivalent to float d_x1 = x0 + w
0498: dx1Num = x0 + w;
0499: dx1Denom = 1;
0500:
0501: // Equivalent to float d_y1 = y0 + h
0502: dy1Num = y0 + h;
0503: dy1Denom = 1;
0504:
0505: } else {
0506:
0507: // First point inside the source
0508: dx0Num = 2 * x0 + 1;
0509: dx0Denom = 2;
0510:
0511: dy0Num = 2 * y0 + 1;
0512: dy0Denom = 2;
0513:
0514: // for other interpolations, x1 = x0+w+0.5, y1 = y0+h+0.5
0515: // as derived in the formulae derivation above.
0516: dx1Num = 2 * x0 + 2 * w + 1;
0517: dx1Denom = 2;
0518:
0519: dy1Num = 2 * y0 + 2 * h + 1;
0520: dy1Denom = 2;
0521:
0522: // Equivalent to x0 += lpad;
0523: dx0Num += dx0Denom * lpad;
0524: // Equivalent to y0 += tpad;
0525: dy0Num += dy0Denom * tpad;
0526:
0527: // Equivalent to x1 -= rpad;
0528: dx1Num -= dx1Denom * rpad;
0529: // Equivalent to y1 += bpad;
0530: dy1Num -= dy1Denom * bpad;
0531: }
0532:
0533: // Forward map the first and last source points
0534:
0535: // Equivalent to float d_x0 = x0 * scaleX;
0536: dx0Num *= scaleXRationalNum;
0537: dx0Denom *= scaleXRationalDenom;
0538:
0539: // Add the X translation factor d_x0 += transX
0540: dx0Num = dx0Num * transXRationalDenom + transXRationalNum
0541: * dx0Denom;
0542: dx0Denom *= transXRationalDenom;
0543:
0544: // Equivalent to float d_y0 = y0 * scaleY;
0545: dy0Num *= scaleYRationalNum;
0546: dy0Denom *= scaleYRationalDenom;
0547:
0548: // Add the Y translation factor, float d_y0 += transY
0549: dy0Num = dy0Num * transYRationalDenom + transYRationalNum
0550: * dy0Denom;
0551: dy0Denom *= transYRationalDenom;
0552:
0553: // Equivalent to float d_x1 = x1 * scaleX;
0554: dx1Num *= scaleXRationalNum;
0555: dx1Denom *= scaleXRationalDenom;
0556:
0557: // Add the X translation factor d_x1 += transX
0558: dx1Num = dx1Num * transXRationalDenom + transXRationalNum
0559: * dx1Denom;
0560: dx1Denom *= transXRationalDenom;
0561:
0562: // Equivalent to float d_y1 = y1 * scaleY;
0563: dy1Num *= scaleYRationalNum;
0564: dy1Denom *= scaleYRationalDenom;
0565:
0566: // Add the Y translation factor, float d_y1 += transY
0567: dy1Num = dy1Num * transYRationalDenom + transYRationalNum
0568: * dy1Denom;
0569: dy1Denom *= transYRationalDenom;
0570:
0571: // Get the integral coordinates
0572:
0573: int l_x0, l_y0, l_x1, l_y1;
0574:
0575: // Subtract 0.5 from dx0, dy0
0576: dx0Num = 2 * dx0Num - dx0Denom;
0577: dx0Denom *= 2;
0578:
0579: dy0Num = 2 * dy0Num - dy0Denom;
0580: dy0Denom *= 2;
0581:
0582: l_x0 = Rational.ceil(dx0Num, dx0Denom);
0583: l_y0 = Rational.ceil(dy0Num, dy0Denom);
0584:
0585: // Subtract 0.5 from dx1, dy1
0586: dx1Num = 2 * dx1Num - dx1Denom;
0587: dx1Denom *= 2;
0588:
0589: dy1Num = 2 * dy1Num - dy1Denom;
0590: dy1Denom *= 2;
0591:
0592: l_x1 = (int) Rational.floor(dx1Num, dx1Denom);
0593: // l_x1 must be less than but not equal to (dx1Num/dx1Denom)
0594: if ((l_x1 * dx1Denom) == dx1Num) {
0595: l_x1 -= 1;
0596: }
0597:
0598: l_y1 = (int) Rational.floor(dy1Num, dy1Denom);
0599: if (l_y1 * dy1Denom == dy1Num) {
0600: l_y1 -= 1;
0601: }
0602:
0603: computableBounds = new Rectangle(l_x0, l_y0,
0604: (l_x1 - l_x0 + 1), (l_y1 - l_y0 + 1));
0605: } else {
0606: // If extender is present we can write the entire destination.
0607: computableBounds = getBounds();
0608: }
0609: }
0610:
0611: /**
0612: * Returns the number of samples required to the left of the center.
0613: *
0614: * @return The left padding factor.
0615: *
0616: * @deprecated as of JAI 1.1.
0617: */
0618: public int getLeftPadding() {
0619: return interp == null ? 0 : interp.getLeftPadding();
0620: }
0621:
0622: /**
0623: * Returns the number of samples required to the right of the center.
0624: *
0625: * @return The right padding factor.
0626: *
0627: * @deprecated as of JAI 1.1.
0628: */
0629: public int getRightPadding() {
0630: return interp == null ? 0 : interp.getRightPadding();
0631: }
0632:
0633: /**
0634: * Returns the number of samples required above the center.
0635: *
0636: * @return The top padding factor.
0637: *
0638: * @deprecated as of JAI 1.1.
0639: */
0640: public int getTopPadding() {
0641: return interp == null ? 0 : interp.getTopPadding();
0642: }
0643:
0644: /**
0645: * Returns the number of samples required below the center.
0646: *
0647: * @return The bottom padding factor.
0648: *
0649: * @deprecated as of JAI 1.1.
0650: */
0651: public int getBottomPadding() {
0652: return interp == null ? 0 : interp.getBottomPadding();
0653: }
0654:
0655: /**
0656: * Computes the position in the specified source that best
0657: * matches the supplied destination image position.
0658: *
0659: * <p>The implementation in this class returns the value of
0660: * <code>pt</code> in the following code snippet:
0661: *
0662: * <pre>
0663: * Point2D pt = (Point2D)destPt.clone();
0664: * pt.setLocation((destPt.getX() - transX + 0.5)/scaleX - 0.5,
0665: * (destPt.getY() - transY + 0.5)/scaleY - 0.5);
0666: * </pre>
0667: *
0668: * Subclasses requiring different behavior should override this
0669: * method.</p>
0670: *
0671: * @param destPt the position in destination image coordinates
0672: * to map to source image coordinates.
0673: * @param sourceIndex the index of the source image.
0674: *
0675: * @return a <code>Point2D</code> of the same class as
0676: * <code>destPt</code>.
0677: *
0678: * @throws IllegalArgumentException if <code>destPt</code> is
0679: * <code>null</code>.
0680: * @throws IndexOutOfBoundsException if <code>sourceIndex</code> is
0681: * non-zero.
0682: *
0683: * @since JAI 1.1.2
0684: */
0685: public Point2D mapDestPoint(Point2D destPt, int sourceIndex) {
0686: if (destPt == null) {
0687: throw new IllegalArgumentException(JaiI18N
0688: .getString("Generic0"));
0689: } else if (sourceIndex != 0) {
0690: throw new IndexOutOfBoundsException(JaiI18N
0691: .getString("Generic1"));
0692: }
0693:
0694: Point2D pt = (Point2D) destPt.clone();
0695:
0696: pt.setLocation((destPt.getX() - transX + 0.5) / scaleX - 0.5,
0697: (destPt.getY() - transY + 0.5) / scaleY - 0.5);
0698:
0699: return pt;
0700: }
0701:
0702: /**
0703: * Computes the position in the destination that best
0704: * matches the supplied source image position.
0705: *
0706: * <p>The implementation in this class returns the value of
0707: * <code>pt</code> in the following code snippet:
0708: *
0709: * <pre>
0710: * Point2D pt = (Point2D)sourcePt.clone();
0711: * pt.setLocation(scaleX*(sourcePt.getX() + 0.5) + transX - 0.5,
0712: * scaleY*(sourcePt.getY() + 0.5) + transY - 0.5);
0713: * </pre>
0714: *
0715: * Subclasses requiring different behavior should override this
0716: * method.</p>
0717: *
0718: * @param sourcePt the position in source image coordinates
0719: * to map to destination image coordinates.
0720: * @param sourceIndex the index of the source image.
0721: *
0722: * @return a <code>Point2D</code> of the same class as
0723: * <code>sourcePt</code>.
0724: *
0725: * @throws IllegalArgumentException if <code>sourcePt</code> is
0726: * <code>null</code>.
0727: * @throws IndexOutOfBoundsException if <code>sourceIndex</code> is
0728: * non-zero.
0729: *
0730: * @since JAI 1.1.2
0731: */
0732: public Point2D mapSourcePoint(Point2D sourcePt, int sourceIndex) {
0733: if (sourcePt == null) {
0734: throw new IllegalArgumentException(JaiI18N
0735: .getString("Generic0"));
0736: } else if (sourceIndex != 0) {
0737: throw new IndexOutOfBoundsException(JaiI18N
0738: .getString("Generic1"));
0739: }
0740:
0741: Point2D pt = (Point2D) sourcePt.clone();
0742:
0743: pt.setLocation(scaleX * (sourcePt.getX() + 0.5) + transX - 0.5,
0744: scaleY * (sourcePt.getY() + 0.5) + transY - 0.5);
0745:
0746: return pt;
0747: }
0748:
0749: /**
0750: * Returns the minimum bounding box of the region of the destination
0751: * to which a particular <code>Rectangle</code> of the specified source
0752: * will be mapped.
0753: *
0754: * @param sourceRect the <code>Rectangle</code> in source coordinates.
0755: * @param sourceIndex the index of the source image.
0756: *
0757: * @return a <code>Rectangle</code> indicating the destination
0758: * bounding box, or <code>null</code> if the bounding box
0759: * is unknown.
0760: *
0761: * @throws IllegalArgumentException if <code>sourceIndex</code> is
0762: * negative or greater than the index of the last source.
0763: * @throws IllegalArgumentException if <code>sourceRect</code> is
0764: * <code>null</code>.
0765: *
0766: * @since JAI 1.1
0767: */
0768: protected Rectangle forwardMapRect(Rectangle sourceRect,
0769: int sourceIndex) {
0770:
0771: if (sourceRect == null) {
0772: throw new IllegalArgumentException(JaiI18N
0773: .getString("Generic0"));
0774: }
0775:
0776: if (sourceIndex != 0) {
0777: throw new IllegalArgumentException(JaiI18N
0778: .getString("Generic1"));
0779: }
0780:
0781: // Get the source dimensions
0782: int x0 = sourceRect.x;
0783: int y0 = sourceRect.y;
0784: int w = sourceRect.width;
0785: int h = sourceRect.height;
0786:
0787: // Variables to represent the first pixel inside the destination.
0788: long dx0Num, dx0Denom, dy0Num, dy0Denom;
0789:
0790: // Variables to represent the last destination pixel.
0791: long dx1Num, dx1Denom, dy1Num, dy1Denom;
0792:
0793: if (interp instanceof InterpolationNearest) {
0794:
0795: // First point inside the source
0796: dx0Num = x0;
0797: dx0Denom = 1;
0798:
0799: dy0Num = y0;
0800: dy0Denom = 1;
0801:
0802: // First point outside source
0803: // for nearest, x1 = x0 + w, y1 = y0 + h
0804: // since anything >= a and < a+1 maps to a for nearest, since
0805: // we use floor to calculate the integral source position
0806:
0807: // Equivalent to float d_x1 = x0 + w
0808: dx1Num = x0 + w;
0809: dx1Denom = 1;
0810:
0811: // Equivalent to float d_y1 = y0 + h
0812: dy1Num = y0 + h;
0813: dy1Denom = 1;
0814:
0815: } else {
0816:
0817: // First point inside the source (x0 + 0.5, y0 + 0.5)
0818: dx0Num = 2 * x0 + 1;
0819: dx0Denom = 2;
0820:
0821: dy0Num = 2 * y0 + 1;
0822: dy0Denom = 2;
0823:
0824: // for other interpolations, x1 = x0 + w + 0.5, y1 = y0 + h + 0.5
0825: // as derived in the formulae derivation above.
0826: dx1Num = 2 * x0 + 2 * w + 1;
0827: dx1Denom = 2;
0828:
0829: dy1Num = 2 * y0 + 2 * h + 1;
0830: dy1Denom = 2;
0831: }
0832:
0833: // Forward map first and last source positions
0834:
0835: // Equivalent to float d_x0 = x0 * scaleX;
0836: dx0Num = dx0Num * scaleXRationalNum;
0837: dx0Denom *= scaleXRationalDenom;
0838:
0839: // Equivalent to float d_y0 = y0 * scaleY;
0840: dy0Num = dy0Num * scaleYRationalNum;
0841: dy0Denom *= scaleYRationalDenom;
0842:
0843: // Equivalent to float d_x1 = x1 * scaleX;
0844: dx1Num = dx1Num * scaleXRationalNum;
0845: dx1Denom *= scaleXRationalDenom;
0846:
0847: // Equivalent to float d_y1 = y1 * scaleY;
0848: dy1Num = dy1Num * scaleYRationalNum;
0849: dy1Denom *= scaleYRationalDenom;
0850:
0851: // Add the translation factors.
0852:
0853: // Equivalent to float d_x0 += transX
0854: dx0Num = dx0Num * transXRationalDenom + transXRationalNum
0855: * dx0Denom;
0856: dx0Denom *= transXRationalDenom;
0857:
0858: // Equivalent to float d_y0 += transY
0859: dy0Num = dy0Num * transYRationalDenom + transYRationalNum
0860: * dy0Denom;
0861: dy0Denom *= transYRationalDenom;
0862:
0863: // Equivalent to float d_x1 += transX
0864: dx1Num = dx1Num * transXRationalDenom + transXRationalNum
0865: * dx1Denom;
0866: dx1Denom *= transXRationalDenom;
0867:
0868: // Equivalent to float d_y1 += transY
0869: dy1Num = dy1Num * transYRationalDenom + transYRationalNum
0870: * dy1Denom;
0871: dy1Denom *= transYRationalDenom;
0872:
0873: // Get the integral coordinates
0874: int l_x0, l_y0, l_x1, l_y1;
0875:
0876: // Subtract 0.5 from dx0, dy0
0877: dx0Num = 2 * dx0Num - dx0Denom;
0878: dx0Denom *= 2;
0879:
0880: dy0Num = 2 * dy0Num - dy0Denom;
0881: dy0Denom *= 2;
0882:
0883: l_x0 = Rational.ceil(dx0Num, dx0Denom);
0884: l_y0 = Rational.ceil(dy0Num, dy0Denom);
0885:
0886: // Subtract 0.5 from dx1, dy1
0887: dx1Num = 2 * dx1Num - dx1Denom;
0888: dx1Denom *= 2;
0889:
0890: dy1Num = 2 * dy1Num - dy1Denom;
0891: dy1Denom *= 2;
0892:
0893: l_x1 = (int) Rational.floor(dx1Num, dx1Denom);
0894: if ((l_x1 * dx1Denom) == dx1Num) {
0895: l_x1 -= 1;
0896: }
0897:
0898: l_y1 = (int) Rational.floor(dy1Num, dy1Denom);
0899: if ((l_y1 * dy1Denom) == dy1Num) {
0900: l_y1 -= 1;
0901: }
0902:
0903: return new Rectangle(l_x0, l_y0, (l_x1 - l_x0 + 1), (l_y1
0904: - l_y0 + 1));
0905: }
0906:
0907: /**
0908: * Returns the minimum bounding box of the region of the specified
0909: * source to which a particular <code>Rectangle</code> of the
0910: * destination will be mapped.
0911: *
0912: * @param destRect the <code>Rectangle</code> in destination coordinates.
0913: * @param sourceIndex the index of the source image.
0914: *
0915: * @return a <code>Rectangle</code> indicating the source bounding box,
0916: * or <code>null</code> if the bounding box is unknown.
0917: *
0918: * @throws IllegalArgumentException if <code>sourceIndex</code> is
0919: * negative or greater than the index of the last source.
0920: * @throws IllegalArgumentException if <code>destRect</code> is
0921: * <code>null</code>.
0922: *
0923: * @since JAI 1.1
0924: */
0925: protected Rectangle backwardMapRect(Rectangle destRect,
0926: int sourceIndex) {
0927:
0928: if (destRect == null) {
0929: throw new IllegalArgumentException(JaiI18N
0930: .getString("Generic0"));
0931: }
0932:
0933: if (sourceIndex != 0) {
0934: throw new IllegalArgumentException(JaiI18N
0935: .getString("Generic1"));
0936: }
0937:
0938: // Get the destination rectangle coordinates and dimensions
0939: int x0 = destRect.x;
0940: int y0 = destRect.y;
0941: int w = destRect.width;
0942: int h = destRect.height;
0943:
0944: // Variables that will eventually hold the source pixel
0945: // positions which are the result of the backward map
0946: long sx0Num, sx0Denom, sy0Num, sy0Denom;
0947:
0948: // First destination point that will be backward mapped
0949: // will be dx0 + 0.5, dy0 + 0.5
0950: sx0Num = (x0 * 2 + 1);
0951: sx0Denom = 2;
0952:
0953: sy0Num = (y0 * 2 + 1);
0954: sy0Denom = 2;
0955:
0956: // The last destination pixel to be backward mapped will be
0957: // dx0 + w - 1 + 0.5, dy0 + h - 1 + 0.5 i.e.
0958: // dx0 + w - 0.5, dy0 + h - 0.5
0959: long sx1Num, sx1Denom, sy1Num, sy1Denom;
0960:
0961: // Equivalent to float sx1 = dx0 + dw - 0.5;
0962: sx1Num = 2 * x0 + 2 * w - 1;
0963: sx1Denom = 2;
0964:
0965: // Equivalent to float sy1 = dy0 + dh - 0.5;
0966: sy1Num = 2 * y0 + 2 * h - 1;
0967: sy1Denom = 2;
0968:
0969: // Subtract the translation factors.
0970: sx0Num = sx0Num * transXRationalDenom - transXRationalNum
0971: * sx0Denom;
0972: sx0Denom *= transXRationalDenom;
0973:
0974: sy0Num = sy0Num * transYRationalDenom - transYRationalNum
0975: * sy0Denom;
0976: sy0Denom *= transYRationalDenom;
0977:
0978: sx1Num = sx1Num * transXRationalDenom - transXRationalNum
0979: * sx1Denom;
0980: sx1Denom *= transXRationalDenom;
0981:
0982: sy1Num = sy1Num * transYRationalDenom - transYRationalNum
0983: * sy1Denom;
0984: sy1Denom *= transYRationalDenom;
0985:
0986: // Backward map both the destination positions
0987:
0988: // Equivalent to float sx0 = x0 / scaleX;
0989: sx0Num *= invScaleXRationalNum;
0990: sx0Denom *= invScaleXRationalDenom;
0991:
0992: sy0Num *= invScaleYRationalNum;
0993: sy0Denom *= invScaleYRationalDenom;
0994:
0995: sx1Num *= invScaleXRationalNum;
0996: sx1Denom *= invScaleXRationalDenom;
0997:
0998: sy1Num *= invScaleYRationalNum;
0999: sy1Denom *= invScaleYRationalDenom;
1000:
1001: int s_x0 = 0, s_y0 = 0, s_x1 = 0, s_y1 = 0;
1002: if (interp instanceof InterpolationNearest) {
1003:
1004: // Floor sx0, sy0
1005: s_x0 = Rational.floor(sx0Num, sx0Denom);
1006: s_y0 = Rational.floor(sy0Num, sy0Denom);
1007:
1008: // Equivalent to (int)Math.floor(sx1)
1009: s_x1 = Rational.floor(sx1Num, sx1Denom);
1010:
1011: // Equivalent to (int)Math.floor(sy1)
1012: s_y1 = Rational.floor(sy1Num, sy1Denom);
1013:
1014: } else {
1015: // For all other interpolations
1016:
1017: // Equivalent to (int) Math.floor(sx0 - 0.5)
1018: s_x0 = Rational.floor(2 * sx0Num - sx0Denom, 2 * sx0Denom);
1019:
1020: // Equivalent to (int) Math.floor(sy0 - 0.5)
1021: s_y0 = Rational.floor(2 * sy0Num - sy0Denom, 2 * sy0Denom);
1022:
1023: // Calculate the last source point
1024: s_x1 = Rational.floor(2 * sx1Num - sx1Denom, 2 * sx1Denom);
1025:
1026: // Equivalent to (int)Math.ceil(sy1 - 0.5)
1027: s_y1 = Rational.floor(2 * sy1Num - sy1Denom, 2 * sy1Denom);
1028: }
1029:
1030: return new Rectangle(s_x0, s_y0, (s_x1 - s_x0 + 1), (s_y1
1031: - s_y0 + 1));
1032: }
1033:
1034: /**
1035: * Computes a tile. If source cobbling was requested at
1036: * construction time, the source tile boundaries are overlayed
1037: * onto the destination, cobbling is performed for areas that
1038: * intersect multiple source tiles, and
1039: * <code>computeRect(Raster[], WritableRaster, Rectangle)</code>
1040: * is called for each of the resulting regions. Otherwise,
1041: * <code>computeRect(PlanarImage[], WritableRaster,
1042: * Rectangle)</code> is called once to compute the entire active
1043: * area of the tile.
1044: *
1045: * <p> The image bounds may be larger than the bounds of the
1046: * source image. In this case, samples for which there are no
1047: * corresponding sources are set to zero.
1048: *
1049: * <p> The following steps are performed in order to compute the tile:
1050: * <ul>
1051: * <li> The destination tile is backward mapped to compute the needed
1052: * source.
1053: * <li> This source is then split on tile boundaries to produce rectangles
1054: * that do not cross tile boundaries.
1055: * <li> These source rectangles are then forward mapped to produce
1056: * destination rectangles, and the computeRect method is called for
1057: * each corresponding pair of source and destination rectangles.
1058: * <li> For higher order interpolations, some source cobbling across tile
1059: * boundaries does occur.
1060: * </ul>
1061: *
1062: * @param tileX The X index of the tile.
1063: * @param tileY The Y index of the tile.
1064: *
1065: * @return The tile as a <code>Raster</code>.
1066: */
1067: public Raster computeTile(int tileX, int tileY) {
1068:
1069: if (!cobbleSources) {
1070: return super .computeTile(tileX, tileY);
1071: }
1072:
1073: // X and Y coordinate of the pixel pixel of the tile.
1074: int orgX = tileXToX(tileX);
1075: int orgY = tileYToY(tileY);
1076:
1077: // Create a new WritableRaster to represent this tile.
1078: WritableRaster dest = createWritableRaster(sampleModel,
1079: new Point(orgX, orgY));
1080:
1081: Rectangle rect = new Rectangle(orgX, orgY, tileWidth,
1082: tileHeight);
1083:
1084: // Clip dest rectangle against the part of the destination
1085: // rectangle that can be written.
1086: Rectangle destRect = rect.intersection(computableBounds);
1087: if ((destRect.width <= 0) || (destRect.height <= 0)) {
1088: // If empty rectangle, return empty tile.
1089: return dest;
1090: }
1091:
1092: // Get the source rectangle required to compute the destRect
1093: Rectangle srcRect = mapDestRect(destRect, 0);
1094: Raster[] sources = new Raster[1];
1095:
1096: // Split the source on tile boundaries.
1097: // Get the new pairs of src & dest Rectangles
1098:
1099: // The tileWidth and tileHeight of the source image
1100: // may differ from this tileWidth and tileHeight.
1101: PlanarImage source0 = getSource(0);
1102:
1103: IntegerSequence srcXSplits = new IntegerSequence();
1104: IntegerSequence srcYSplits = new IntegerSequence();
1105: source0.getSplits(srcXSplits, srcYSplits, srcRect);
1106:
1107: if (srcXSplits.getNumElements() == 1
1108: && srcYSplits.getNumElements() == 1) {
1109:
1110: // If the source is fully contained within
1111: // a tile there is no need to split it any further.
1112: if (extender == null) {
1113: sources[0] = source0.getData(srcRect);
1114: } else {
1115: sources[0] = source0.getExtendedData(srcRect, extender);
1116: }
1117:
1118: // Compute the destination tile.
1119: computeRect(sources, dest, destRect);
1120: } else {
1121: // Source Rect straddles 2 or more tiles
1122:
1123: // Get Source Tilewidth & height
1124: int srcTileWidth = source0.getTileWidth();
1125: int srcTileHeight = source0.getTileHeight();
1126:
1127: srcYSplits.startEnumeration();
1128: while (srcYSplits.hasMoreElements()) {
1129: // Along Y TileBoundaries
1130: int ysplit = srcYSplits.nextElement();
1131:
1132: srcXSplits.startEnumeration();
1133: while (srcXSplits.hasMoreElements()) {
1134: // Along X TileBoundaries
1135: int xsplit = srcXSplits.nextElement();
1136:
1137: // Construct a pseudo tile for intersection purposes
1138: Rectangle srcTile = new Rectangle(xsplit, ysplit,
1139: srcTileWidth, srcTileHeight);
1140:
1141: // Intersect the tile with the source Rectangle
1142: Rectangle newSrcRect = srcRect
1143: .intersection(srcTile);
1144:
1145: //
1146: // The new source rect could be of size less than or equal
1147: // to the interpolation kernel dimensions. In which case
1148: // the forward map produces null destination rectangles.
1149: // Hence we need to deal with these cases in the manner
1150: // implemented below (grow the source before the map. This
1151: // would result in source cobbling). Not an issue for
1152: // Nearest-Neighbour.
1153: //
1154: if (!(interp instanceof InterpolationNearest)) {
1155:
1156: if (newSrcRect.width <= interp.getWidth()) {
1157:
1158: //
1159: // Need to forward map this source rectangle.
1160: // Since we need a minimum of 2 * (lpad + rpad + 1)
1161: // in order to process, we contsruct a source
1162: // rectangle of that size, forward map and then
1163: // process the resulting destination rectangle.
1164: //
1165: Rectangle wSrcRect = new Rectangle();
1166: Rectangle wDestRect;
1167:
1168: wSrcRect.x = newSrcRect.x;
1169: wSrcRect.y = newSrcRect.y - tpad - 1;
1170: wSrcRect.width = 2 * (lpad + rpad + 1);
1171: wSrcRect.height = newSrcRect.height + bpad
1172: + tpad + 2;
1173:
1174: wSrcRect = wSrcRect.intersection(source0
1175: .getBounds());
1176:
1177: wDestRect = mapSourceRect(wSrcRect, 0);
1178:
1179: //
1180: // Make sure this destination rectangle is
1181: // within the bounds of our original writable
1182: // destination rectangle
1183: //
1184: wDestRect = wDestRect
1185: .intersection(destRect);
1186:
1187: if ((wDestRect.width > 0)
1188: && (wDestRect.height > 0)) {
1189: // Do the operations with these new rectangles
1190: if (extender == null) {
1191: sources[0] = source0
1192: .getData(wSrcRect);
1193: } else {
1194: sources[0] = source0
1195: .getExtendedData(wSrcRect,
1196: extender);
1197: }
1198:
1199: // Compute the destination tile.
1200: computeRect(sources, dest, wDestRect);
1201: }
1202: }
1203:
1204: if (newSrcRect.height <= interp.getHeight()) {
1205:
1206: //
1207: // Need to forward map this source rectangle.
1208: // Since we need a minimum of 2 * (tpad + bpad + 1)
1209: // in order to process, we create a source
1210: // rectangle of that size, forward map and then
1211: // process the resulting destinaltion rectangle
1212: //
1213: Rectangle hSrcRect = new Rectangle();
1214: Rectangle hDestRect;
1215:
1216: hSrcRect.x = newSrcRect.x - lpad - 1;
1217: hSrcRect.y = newSrcRect.y;
1218: hSrcRect.width = newSrcRect.width + lpad
1219: + rpad + 2;
1220: hSrcRect.height = 2 * (tpad + bpad + 1);
1221:
1222: hSrcRect = hSrcRect.intersection(source0
1223: .getBounds());
1224:
1225: hDestRect = mapSourceRect(hSrcRect, 0);
1226:
1227: //
1228: // Make sure this destination rectangle is
1229: // within the bounds of our original writable
1230: // destination rectangle
1231: //
1232: hDestRect = hDestRect
1233: .intersection(destRect);
1234:
1235: if ((hDestRect.width > 0)
1236: && (hDestRect.height > 0)) {
1237: // Do the operations with these new rectangles
1238: if (extender == null) {
1239: sources[0] = source0
1240: .getData(hSrcRect);
1241: } else {
1242: sources[0] = source0
1243: .getExtendedData(hSrcRect,
1244: extender);
1245: }
1246:
1247: // Compute the destination tile.
1248: computeRect(sources, dest, hDestRect);
1249: }
1250: }
1251: }
1252:
1253: // Process source rectangle
1254: if ((newSrcRect.width > 0)
1255: && (newSrcRect.height > 0)) {
1256:
1257: // Forward map this source rectangle
1258: // to get the destination rectangle
1259: Rectangle newDestRect = mapSourceRect(
1260: newSrcRect, 0);
1261:
1262: // Make sure this destination rectangle is
1263: // within the bounds of our original writable
1264: // destination rectangle
1265: newDestRect = newDestRect
1266: .intersection(destRect);
1267:
1268: if ((newDestRect.width > 0)
1269: && (newDestRect.height > 0)) {
1270:
1271: // Do the operations with these new rectangles
1272: if (extender == null) {
1273: sources[0] = source0
1274: .getData(newSrcRect);
1275: } else {
1276: sources[0] = source0.getExtendedData(
1277: newSrcRect, extender);
1278: }
1279:
1280: // Compute the destination tile.
1281: computeRect(sources, dest, newDestRect);
1282: }
1283:
1284: //
1285: // Since mapSourceRect (forward map) shrinks the
1286: // source rectangle before the map, there are areas
1287: // of this rectangle which never get mapped.
1288: //
1289: // These occur at the tile boundaries between
1290: // rectangles. The following algorithm handles
1291: // these edge conditions.
1292: //
1293: // The cases :
1294: // Right edge
1295: // Bottom edge
1296: // Lower Right Corner
1297: //
1298:
1299: if (!(interp instanceof InterpolationNearest)) {
1300: Rectangle RTSrcRect = new Rectangle();
1301: Rectangle RTDestRect;
1302:
1303: // Right Edge
1304: RTSrcRect.x = newSrcRect.x
1305: + newSrcRect.width - 1 - rpad
1306: - lpad;
1307: RTSrcRect.y = newSrcRect.y;
1308:
1309: //
1310: // The amount of src not used from the end of
1311: // the first tile is rpad + 0.5. The amount
1312: // not used from the beginning of the next tile
1313: // is lpad + 0.5. Since we cannot start mapping
1314: // at 0.5, we need to get the area of the half
1315: // pixel on both sides. So we get another 0,5
1316: // from both sides. In total (rpad + 0.5 +
1317: // 0.5) + (lpad + 0.5 + 0.5)
1318: // Since mapSourceRect subtracts rpad + 0.5 and
1319: // lpad + 0.5 from the source before the
1320: // forward map, we need to add that in.
1321: //
1322: RTSrcRect.width = 2 * (lpad + rpad + 1);
1323: RTSrcRect.height = newSrcRect.height;
1324:
1325: RTDestRect = mapSourceRect(RTSrcRect, 0);
1326:
1327: // Clip this against the whole destrect
1328: RTDestRect = RTDestRect
1329: .intersection(destRect);
1330:
1331: // RTSrcRect may be out of image bounds;
1332: // map one more time
1333: RTSrcRect = mapDestRect(RTDestRect, 0);
1334:
1335: if (RTDestRect.width > 0
1336: && RTDestRect.height > 0) {
1337: // Do the operations with these new rectangles
1338: if (extender == null) {
1339: sources[0] = source0
1340: .getData(RTSrcRect);
1341: } else {
1342: sources[0] = source0
1343: .getExtendedData(RTSrcRect,
1344: extender);
1345: }
1346:
1347: computeRect(sources, dest, RTDestRect);
1348: }
1349:
1350: // Bottom Edge
1351: Rectangle BTSrcRect = new Rectangle();
1352: Rectangle BTDestRect;
1353:
1354: BTSrcRect.x = newSrcRect.x;
1355: BTSrcRect.y = newSrcRect.y
1356: + newSrcRect.height - 1 - bpad
1357: - tpad;
1358:
1359: //
1360: // The amount of src not used from the end of
1361: // the first tile is tpad + 0.5. The amount
1362: // not used from the beginning of the next tile
1363: // is bpad + 0.5. Since we cannot start mapping
1364: // at 0.5, we need to get the area of the half
1365: // pixel on both sides. So we get another 0,5
1366: // from both sides. In total (tpad + 0.5 +
1367: // 0.5) + (bpad + 0.5 + 0.5)
1368: // Since mapSourceRect subtracts tpad + 0.5 and
1369: // bpad + 0.5 from the source before the
1370: // forward map, we need to add that in.
1371: //
1372: BTSrcRect.width = newSrcRect.width;
1373: BTSrcRect.height = 2 * (tpad + bpad + 1);
1374:
1375: BTDestRect = mapSourceRect(BTSrcRect, 0);
1376:
1377: // Clip this against the whole destrect
1378: BTDestRect = BTDestRect
1379: .intersection(destRect);
1380:
1381: //BTSrcRect maybe out of bounds
1382: //map one more time
1383: BTSrcRect = mapDestRect(BTDestRect, 0);
1384: //end
1385:
1386: if (BTDestRect.width > 0
1387: && BTDestRect.height > 0) {
1388:
1389: // Do the operations with these new rectangles
1390: if (extender == null) {
1391: sources[0] = source0
1392: .getData(BTSrcRect);
1393: } else {
1394: sources[0] = source0
1395: .getExtendedData(BTSrcRect,
1396: extender);
1397: }
1398:
1399: computeRect(sources, dest, BTDestRect);
1400: }
1401:
1402: // Lower Right Area
1403: Rectangle LRTSrcRect = new Rectangle();
1404: Rectangle LRTDestRect;
1405:
1406: LRTSrcRect.x = newSrcRect.x
1407: + newSrcRect.width - 1 - rpad
1408: - lpad;
1409: LRTSrcRect.y = newSrcRect.y
1410: + newSrcRect.height - 1 - bpad
1411: - tpad;
1412:
1413: // Comment forthcoming
1414: LRTSrcRect.width = 2 * (rpad + lpad + 1);
1415: LRTSrcRect.height = 2 * (tpad + bpad + 1);
1416:
1417: LRTDestRect = mapSourceRect(LRTSrcRect, 0);
1418:
1419: // Clip this against the whole destrect
1420: LRTDestRect = LRTDestRect
1421: .intersection(destRect);
1422:
1423: // LRTSrcRect may still be out of bounds
1424: LRTSrcRect = mapDestRect(LRTDestRect, 0);
1425:
1426: if (LRTDestRect.width > 0
1427: && LRTDestRect.height > 0) {
1428: // Do the operations with these new rectangles
1429: if (extender == null) {
1430: sources[0] = source0
1431: .getData(LRTSrcRect);
1432: } else {
1433: sources[0] = source0
1434: .getExtendedData(
1435: LRTSrcRect,
1436: extender);
1437: }
1438:
1439: computeRect(sources, dest, LRTDestRect);
1440: }
1441: }
1442: }
1443: }
1444: }
1445: }
1446:
1447: // Return the written destination raster
1448: return dest;
1449: }
1450: }
|