/* * $RCSfile: WarpGeneralOpImage.java,v $ * * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * Use is subject to license terms. * * $Revision: 1.1 $ * $Date: 2005/02/11 04:56:47 $ * $State: Exp $ */ package com.lightcrafts.media.jai.opimage; import java.awt.Rectangle; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.IndexColorModel; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import com.lightcrafts.mediax.jai.BorderExtender; import com.lightcrafts.mediax.jai.ImageLayout; import com.lightcrafts.mediax.jai.Interpolation; import com.lightcrafts.mediax.jai.PlanarImage; import com.lightcrafts.mediax.jai.RasterAccessor; import com.lightcrafts.mediax.jai.RasterFormatTag; import java.util.Map; import com.lightcrafts.mediax.jai.Warp; import com.lightcrafts.mediax.jai.WarpOpImage; import com.lightcrafts.mediax.jai.iterator.RandomIter; import com.lightcrafts.mediax.jai.iterator.RandomIterFactory; import com.lightcrafts.media.jai.util.ImageUtil; /** * An OpImage implementing the general "Warp" operation as * described in com.lightcrafts.mediax.jai.operator.WarpDescriptor. * It supports all interpolation cases. * * @since EA2 * @see com.lightcrafts.mediax.jai.Warp * @see com.lightcrafts.mediax.jai.WarpOpImage * @see com.lightcrafts.mediax.jai.operator.WarpDescriptor * @see WarpRIF * */ final class WarpGeneralOpImage extends WarpOpImage { /** Color table representing source's IndexColorModel. */ private byte[][] ctable = null; /** * Constructs a WarpGeneralOpImage. * * @param source The source image. * @param extender A BorderExtender, or null. * @param layout The destination image layout. * @param warp An object defining the warp algorithm. * @param interp An object describing the interpolation method. */ public WarpGeneralOpImage(RenderedImage source, BorderExtender extender, Map config, ImageLayout layout, Warp warp, Interpolation interp, double[] backgroundValues) { super(source, layout, config, false, extender, interp, warp, backgroundValues); /* * If the source has IndexColorModel, get the RGB color table. * Note, in this case, the source should have an integral data type. * And dest always has data type byte. */ ColorModel srcColorModel = source.getColorModel(); if (srcColorModel instanceof IndexColorModel) { IndexColorModel icm = (IndexColorModel)srcColorModel; ctable = new byte[3][icm.getMapSize()]; icm.getReds(ctable[0]); icm.getGreens(ctable[1]); icm.getBlues(ctable[2]); } } /** Warps a rectangle. */ protected void computeRect(PlanarImage[] sources, WritableRaster dest, Rectangle destRect) { // Retrieve format tags. RasterFormatTag[] formatTags = getFormatTags(); RasterAccessor d = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); switch (d.getDataType()) { case DataBuffer.TYPE_BYTE: computeRectByte(sources[0], d); break; case DataBuffer.TYPE_USHORT: computeRectUShort(sources[0], d); break; case DataBuffer.TYPE_SHORT: computeRectShort(sources[0], d); break; case DataBuffer.TYPE_INT: computeRectInt(sources[0], d); break; case DataBuffer.TYPE_FLOAT: computeRectFloat(sources[0], d); break; case DataBuffer.TYPE_DOUBLE: computeRectDouble(sources[0], d); break; } if (d.isDataCopy()) { d.clampDataArrays(); d.copyDataToRaster(); } } private void computeRectByte(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); byte[][] data = dst.getByteDataArrays(); int precH = 1 << interp.getSubsampleBitsH(); int precV = 1 << interp.getSubsampleBitsV(); float[] warpData = new float[2 * dstWidth]; int[][] samples = new int[kheight][kwidth]; int lineOffset = 0; byte[] backgroundByte = new byte[dstBands]; for (int i = 0; i < dstBands; i++) backgroundByte[i] = (byte)backgroundValues[i]; if (ctable == null) { // source does not have IndexColorModel for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundByte[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSample( xint+i, yint+j, b) & 0xFF; } } data[b][pixelOffset+bandOffsets[b]] = ImageUtil.clampByte( interp.interpolate(samples, xfrac, yfrac)); } } pixelOffset += pixelStride; } } } else { // source has IndexColorModel for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundByte[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { byte[] t = ctable[b]; for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = t[iter.getSample( xint+i, yint+j, 0) & 0xFF] & 0xFF; } } data[b][pixelOffset+bandOffsets[b]] = ImageUtil.clampByte( interp.interpolate(samples, xfrac, yfrac)); } } pixelOffset += pixelStride; } } } } private void computeRectUShort(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); short[][] data = dst.getShortDataArrays(); int precH = 1 << interp.getSubsampleBitsH(); int precV = 1 << interp.getSubsampleBitsV(); float[] warpData = new float[2 * dstWidth]; int[][] samples = new int[kheight][kwidth]; int lineOffset = 0; short[] backgroundUShort = new short[dstBands]; for (int i = 0; i < dstBands; i++) backgroundUShort[i] = (short)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundUShort[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSample( xint+i, yint+j, b) & 0xFFFF; } } data[b][pixelOffset+bandOffsets[b]] = ImageUtil.clampUShort( interp.interpolate(samples, xfrac, yfrac)); } } pixelOffset += pixelStride; } } } private void computeRectShort(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); short[][] data = dst.getShortDataArrays(); int precH = 1 << interp.getSubsampleBitsH(); int precV = 1 << interp.getSubsampleBitsV(); float[] warpData = new float[2 * dstWidth]; int[][] samples = new int[kheight][kwidth]; int lineOffset = 0; short[] backgroundShort = new short[dstBands]; for (int i = 0; i < dstBands; i++) backgroundShort[i] = (short)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundShort[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSample( xint+i, yint+j, b); } } data[b][pixelOffset+bandOffsets[b]] = ImageUtil.clampShort( interp.interpolate(samples, xfrac, yfrac)); } } pixelOffset += pixelStride; } } } private void computeRectInt(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); int[][] data = dst.getIntDataArrays(); int precH = 1 << interp.getSubsampleBitsH(); int precV = 1 << interp.getSubsampleBitsV(); float[] warpData = new float[2 * dstWidth]; int[][] samples = new int[kheight][kwidth]; int lineOffset = 0; int[] backgroundInt = new int[dstBands]; for (int i = 0; i < dstBands; i++) backgroundInt[i] = (int)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); int xfrac = (int)((sx - xint) * precH); int yfrac = (int)((sy - yint) * precV); if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundInt[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSample( xint+i, yint+j, b); } } data[b][pixelOffset+bandOffsets[b]] = interp.interpolate(samples, xfrac, yfrac); } } pixelOffset += pixelStride; } } } private void computeRectFloat(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); float[][] data = dst.getFloatDataArrays(); float[] warpData = new float[2 * dstWidth]; float[][] samples = new float[kheight][kwidth]; int lineOffset = 0; float[] backgroundFloat = new float[dstBands]; for (int i = 0; i < dstBands; i++) backgroundFloat[i] = (float)backgroundValues[i]; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundFloat[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSampleFloat( xint+i, yint+j, b); } } data[b][pixelOffset+bandOffsets[b]] = interp.interpolate(samples, xfrac, yfrac); } } pixelOffset += pixelStride; } } } private void computeRectDouble(PlanarImage src, RasterAccessor dst) { int lpad, rpad, tpad, bpad; if(interp != null) { lpad = interp.getLeftPadding(); rpad = interp.getRightPadding(); tpad = interp.getTopPadding(); bpad = interp.getBottomPadding(); } else { lpad = rpad = tpad = bpad = 0; } int minX, maxX, minY, maxY; RandomIter iter; if(extender != null) { minX = src.getMinX(); maxX = src.getMaxX(); minY = src.getMinY(); maxY = src.getMaxY(); Rectangle bounds = new Rectangle(src.getMinX() - lpad, src.getMinY() - tpad, src.getWidth() + lpad + rpad, src.getHeight() + tpad + bpad); iter = RandomIterFactory.create(src.getExtendedData(bounds, extender), bounds); } else { minX = src.getMinX() + lpad; maxX = src.getMaxX() - rpad; minY = src.getMinY() + tpad; maxY = src.getMaxY() - bpad; iter = RandomIterFactory.create(src, src.getBounds()); } int kwidth = interp.getWidth(); int kheight = interp.getHeight(); int dstWidth = dst.getWidth(); int dstHeight = dst.getHeight(); int dstBands = dst.getNumBands(); int lineStride = dst.getScanlineStride(); int pixelStride = dst.getPixelStride(); int[] bandOffsets = dst.getBandOffsets(); double[][] data = dst.getDoubleDataArrays(); float[] warpData = new float[2 * dstWidth]; double[][] samples = new double[kheight][kwidth]; int lineOffset = 0; for (int h = 0; h < dstHeight; h++) { int pixelOffset = lineOffset; lineOffset += lineStride; warp.warpRect(dst.getX(), dst.getY()+h, dstWidth, 1, warpData); int count = 0; for (int w = 0; w < dstWidth; w++) { float sx = warpData[count++]; float sy = warpData[count++]; int xint = floor(sx); int yint = floor(sy); float xfrac = sx - xint; float yfrac = sy - yint; if (xint < minX || xint >= maxX || yint < minY || yint >= maxY) { /* Fill with a background color. */ if (setBackground) { for (int b = 0; b < dstBands; b++) { data[b][pixelOffset+bandOffsets[b]] = backgroundValues[b]; } } } else { xint -= lpad; yint -= tpad; for (int b = 0; b < dstBands; b++) { for (int j = 0; j < kheight; j++) { for (int i = 0; i < kwidth; i++) { samples[j][i] = iter.getSampleDouble( xint+i, yint+j, b); } } data[b][pixelOffset+bandOffsets[b]] = interp.interpolate(samples, xfrac, yfrac); } } pixelOffset += pixelStride; } } } /** Returns the "floor" value of a float. */ private static final int floor(float f) { return f >= 0 ? (int)f : (int)f - 1; } }