0001: /*
0002: * $RCSfile: OrderedDitherOpImage.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:56:38 $
0010: * $State: Exp $
0011: */
0012: package com.sun.media.jai.opimage;
0013:
0014: import java.awt.Rectangle;
0015: import java.awt.image.ColorModel;
0016: import java.awt.image.DataBuffer;
0017: import java.awt.image.IndexColorModel;
0018: import java.awt.image.MultiPixelPackedSampleModel;
0019: import java.awt.image.Raster;
0020: import java.awt.image.RenderedImage;
0021: import java.awt.image.SampleModel;
0022: import java.awt.image.WritableRaster;
0023: import java.lang.ref.SoftReference;
0024: import java.util.Arrays;
0025: import java.util.Map;
0026: import java.util.Vector;
0027: import javax.media.jai.ColorCube;
0028: import javax.media.jai.ImageLayout;
0029: import javax.media.jai.KernelJAI;
0030: import javax.media.jai.OpImage;
0031: import javax.media.jai.PointOpImage;
0032: import javax.media.jai.RasterAccessor;
0033: import javax.media.jai.RasterFormatTag;
0034: import javax.media.jai.RasterFactory;
0035: import com.sun.media.jai.util.JDKWorkarounds;
0036: import com.sun.media.jai.util.ImageUtil;
0037:
0038: /**
0039: * An <code>OpImage</code> implementing the ordered dither operation as
0040: * described in <code>javax.media.jai.operator.OrderedDitherDescriptor</code>.
0041: *
0042: * <p>This <code>OpImage</code> performs dithering of its source image into
0043: * a single band image using a specified color cube and dither mask.
0044: *
0045: * @see javax.media.jai.KernelJAI
0046: * @see javax.media.jai.ColorCube
0047: *
0048: * @since EA3
0049: *
0050: */
0051: final class OrderedDitherOpImage extends PointOpImage {
0052: /**
0053: * Flag indicating that the generic implementation is used.
0054: */
0055: private static final int TYPE_OD_GENERAL = 0;
0056:
0057: /**
0058: * Flag indicating that the optimized three-band implementation is used
0059: * (byte data only).
0060: */
0061: private static final int TYPE_OD_BYTE_LUT_3BAND = 1;
0062:
0063: /**
0064: * Flag indicating that the optimized N-band implementation is used
0065: * (byte data only).
0066: */
0067: private static final int TYPE_OD_BYTE_LUT_NBAND = 2;
0068:
0069: /**
0070: * Maximim dither LUT size: 16x16 4-band byte dither mask.
0071: */
0072: private static final int DITHER_LUT_LENGTH_MAX = 16 * 16 * 4 * 256;
0073:
0074: /**
0075: * The maximum number of elements in the <code>DitherLUT</code> cache.
0076: */
0077: private static final int DITHER_LUT_CACHE_LENGTH_MAX = 4;
0078:
0079: /**
0080: * A cache of <code>SoftReference</code>s to <code>DitherLUT</code>
0081: * inner class instances.
0082: */
0083: private static Vector ditherLUTCache = new Vector(0,
0084: DITHER_LUT_CACHE_LENGTH_MAX);
0085:
0086: /**
0087: * Flag indicating the implementation to be used.
0088: */
0089: private int odType = TYPE_OD_GENERAL;
0090:
0091: /**
0092: * The number of bands in the source image.
0093: */
0094: protected int numBands;
0095:
0096: /**
0097: * The array of color cube dimensions-less-one.
0098: */
0099: protected int[] dims;
0100:
0101: /**
0102: * The array of color cube multipliers.
0103: */
0104: protected int[] mults;
0105:
0106: /**
0107: * The adjusted offset of the color cube.
0108: */
0109: protected int adjustedOffset;
0110:
0111: /**
0112: * The width of the dither mask.
0113: */
0114: protected int maskWidth;
0115:
0116: /**
0117: * The height of the dither mask.
0118: */
0119: protected int maskHeight;
0120:
0121: /**
0122: * The dither mask matrix scaled by 255.
0123: */
0124: protected byte[][] maskDataByte;
0125:
0126: /**
0127: * The dither mask matrix scaled to USHORT range.
0128: */
0129: protected int[][] maskDataInt;
0130:
0131: /**
0132: * The dither mask matrix scaled to "unsigned int" range.
0133: */
0134: protected long[][] maskDataLong;
0135:
0136: /**
0137: * The dither mask matrix.
0138: */
0139: protected float[][] maskDataFloat;
0140:
0141: /**
0142: * An inner class instance representing a dither lookup table. Used
0143: * for byte data only when the table size is within a specified limit.
0144: */
0145: protected DitherLUT odLUT = null;
0146:
0147: /**
0148: * Force the destination image to be single-banded.
0149: */
0150: private static ImageLayout layoutHelper(ImageLayout layout,
0151: RenderedImage source, ColorCube colorMap) {
0152: ImageLayout il;
0153: if (layout == null) {
0154: il = new ImageLayout(source);
0155: } else {
0156: il = (ImageLayout) layout.clone();
0157: }
0158:
0159: // Get the SampleModel.
0160: SampleModel sm = il.getSampleModel(source);
0161:
0162: // Ensure an appropriate SampleModel.
0163: if (colorMap.getNumBands() == 1
0164: && colorMap.getNumEntries() == 2
0165: && !ImageUtil.isBinary(il.getSampleModel(source))) {
0166: sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
0167: il.getTileWidth(source), il.getTileHeight(source),
0168: 1);
0169: il.setSampleModel(sm);
0170: }
0171:
0172: // Make sure that this OpImage is single-banded.
0173: if (sm.getNumBands() != 1) {
0174: // TODO: Force to SHORT or USHORT if FLOAT or DOUBLE?
0175: sm = RasterFactory.createComponentSampleModel(sm, sm
0176: .getTransferType(), sm.getWidth(), sm.getHeight(),
0177: 1);
0178: il.setSampleModel(sm);
0179:
0180: // Clear the ColorModel mask if needed.
0181: ColorModel cm = il.getColorModel(null);
0182: if (cm != null
0183: && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
0184: // Clear the mask bit if incompatible.
0185: il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
0186: }
0187: }
0188:
0189: // Set an IndexColorModel on the image if:
0190: // a. none is provided in the layout;
0191: // b. source, destination, and colormap have byte data type;
0192: // c. the colormap has 3 bands; and
0193: // d. the source ColorModel is either null or is non-null
0194: // and has a ColorSpace equal to CS_sRGB.
0195: if ((layout == null || !il
0196: .isValid(ImageLayout.COLOR_MODEL_MASK))
0197: && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE
0198: && il.getSampleModel(null).getDataType() == DataBuffer.TYPE_BYTE
0199: && colorMap.getDataType() == DataBuffer.TYPE_BYTE
0200: && colorMap.getNumBands() == 3) {
0201: ColorModel cm = source.getColorModel();
0202: if (cm == null
0203: || (cm != null && cm.getColorSpace().isCS_sRGB())) {
0204: int size = colorMap.getNumEntries();
0205: byte[][] cmap = new byte[3][256];
0206: for (int i = 0; i < 3; i++) {
0207: byte[] band = cmap[i];
0208: byte[] data = colorMap.getByteData(i);
0209: int offset = colorMap.getOffset(i);
0210: int end = offset + size;
0211: for (int j = 0; j < offset; j++) {
0212: band[j] = (byte) 0;
0213: }
0214: for (int j = offset; j < end; j++) {
0215: band[j] = data[j - offset];
0216: }
0217: for (int j = end; j < 256; j++) {
0218: band[j] = (byte) 0xFF;
0219: }
0220: }
0221:
0222: il.setColorModel(new IndexColorModel(8, 256, cmap[0],
0223: cmap[1], cmap[2]));
0224: }
0225: }
0226:
0227: return il;
0228: }
0229:
0230: /**
0231: * Constructs an OrderedDitherOpImage object. May be used to convert a
0232: * single- or multi-band image into a single-band image with a color map.
0233: *
0234: * <p>The image dimensions are derived from the source image. The tile
0235: * grid layout, SampleModel, and ColorModel may optionally be specified
0236: * by an ImageLayout object.
0237: *
0238: * @param source A RenderedImage.
0239: * @param layout An ImageLayout optionally containing the tile grid layout,
0240: * SampleModel, and ColorModel, or null.
0241: * @param colorMap The color map to use which must have a number of bands
0242: * equal to the number of bands in the source image. The offset of this
0243: * <code>ColorCube</code> must be the same for all bands.
0244: * @param ditherMask An an array of <code>KernelJAI</code> objects the
0245: * dimension of which must equal the number of bands in the source image.
0246: * The <i>n</i>th element of the array contains a <code>KernelJAI</code>
0247: * object which represents the dither mask matrix for the corresponding
0248: * band. All <code>KernelJAI</code> objects in the array must have the
0249: * same dimensions and contain floating point values between 0.0F and 1.0F.
0250: */
0251: public OrderedDitherOpImage(RenderedImage source, Map config,
0252: ImageLayout layout, ColorCube colorMap,
0253: KernelJAI[] ditherMask) {
0254: // Construct as a PointOpImage.
0255: super (source, layoutHelper(layout, source, colorMap), config,
0256: true);
0257:
0258: // Initialize the instance variables derived from the color map.
0259: numBands = colorMap.getNumBands();
0260: mults = (int[]) colorMap.getMultipliers().clone();
0261: dims = (int[]) colorMap.getDimsLessOne().clone();
0262: adjustedOffset = colorMap.getAdjustedOffset();
0263:
0264: // Initialize the instance variables derived from the dither mask.
0265: maskWidth = ditherMask[0].getWidth();
0266: maskHeight = ditherMask[0].getHeight();
0267:
0268: // Initialize the data required to effect the operation.
0269: // XXX Postpone until first invocation of computeRect()?
0270: initializeDitherData(sampleModel.getTransferType(), ditherMask);
0271:
0272: // Set flag to permit in-place operation.
0273: permitInPlaceOperation();
0274: }
0275:
0276: /**
0277: * An inner class represting a lookup table to be used in the optimized
0278: * implementations of ordered dithering of byte data.
0279: */
0280: private class DitherLUT {
0281: // Clones of color cube and dither mask data used to create the
0282: // dithering lookup table.
0283: private int[] dimsCache;
0284: private int[] multsCache;
0285: private byte[][] maskDataCache;
0286:
0287: // Stride values of the dither lookup table.
0288: public int ditherLUTBandStride;
0289: public int ditherLUTRowStride;
0290: public int ditherLUTColStride;
0291:
0292: // The dither lookup table.
0293: public byte[] ditherLUT;
0294:
0295: /**
0296: * Create an inner class object representing an ordered dither
0297: * lookup table for byte data.
0298: *
0299: * @param dims The color cube dimensions less one.
0300: * @param mults The color cube multipliers.
0301: * @param maskData The dither mask data scaled to byte range.
0302: */
0303: DitherLUT(int[] dims, int[] mults, byte[][] maskData) {
0304: // Clone the constructor parameters.
0305: dimsCache = (int[]) dims.clone();
0306: multsCache = (int[]) mults.clone();
0307: maskDataCache = new byte[maskData.length][];
0308: for (int i = 0; i < maskData.length; i++) {
0309: maskDataCache[i] = (byte[]) maskData[i].clone();
0310: }
0311:
0312: // Set dither lookup table stride values.
0313: ditherLUTColStride = 256;
0314: ditherLUTRowStride = maskWidth * ditherLUTColStride;
0315: ditherLUTBandStride = maskHeight * ditherLUTRowStride;
0316:
0317: //
0318: // Construct the big dither table. If indexed as a
0319: // multi-dimensional array this would be equivalent to:
0320: //
0321: // ditherLUT[band][ditherRow][ditherColumn][grayLevel]
0322: //
0323: // where ditherRow, Col are modulo the dither mask size.
0324: //
0325: // To minimize the table construction cost, precalculate
0326: // the bin value for a given band and gray level. Then use
0327: // the dithermask threshold value to determine whether to bump
0328: // the value up one level. Thus most of the work is done in the
0329: // outer loops, with a simple comparison left for the inner loop.
0330: //
0331: ditherLUT = new byte[numBands * ditherLUTBandStride];
0332:
0333: int pDithBand = 0;
0334: int maskSize2D = maskWidth * maskHeight;
0335: for (int band = 0; band < numBands; band++) {
0336: int step = dims[band];
0337: int delta = mults[band];
0338: byte[] maskDataBand = maskData[band];
0339: int sum = 0;
0340: for (int gray = 0; gray < 256; gray++) {
0341: int tmp = sum;
0342: int frac = (int) (tmp & 0xff);
0343: int bin = tmp >> 8;
0344: int lowVal = bin * delta;
0345: int highVal = lowVal + delta;
0346: int pDith = pDithBand + gray;
0347: for (int dcount = 0; dcount < maskSize2D; dcount++) {
0348: int threshold = maskDataBand[dcount] & 0xff;
0349: if (frac > threshold) {
0350: ditherLUT[pDith] = (byte) (highVal & 0xff);
0351: } else {
0352: ditherLUT[pDith] = (byte) (lowVal & 0xff);
0353: }
0354: pDith += 256;
0355: } // end dithermask entry
0356: sum += step;
0357: } // end gray level
0358: pDithBand += ditherLUTBandStride;
0359: } // end band
0360: }
0361:
0362: /**
0363: * Determine whether the internal table of this <code>DitherLUT</code>
0364: * is the same as that which would be generated using the supplied
0365: * parameters.
0366: *
0367: * @param dims The color cube dimensions less one.
0368: * @param mults The color cube multipliers.
0369: * @param maskData The dither mask data scaled to byte range.
0370: *
0371: * @return Value indicating equivalence of dither LUTs.
0372: */
0373: public boolean equals(int[] dims, int[] mults, byte[][] maskData) {
0374: // Check dimensions.
0375: if (dims.length != dimsCache.length) {
0376: return false;
0377: }
0378:
0379: for (int i = 0; i < dims.length; i++) {
0380: if (dims[i] != dimsCache[i])
0381: return false;
0382: }
0383:
0384: // Check multipliers.
0385: if (mults.length != multsCache.length) {
0386: return false;
0387: }
0388:
0389: for (int i = 0; i < mults.length; i++) {
0390: if (mults[i] != multsCache[i])
0391: return false;
0392: }
0393:
0394: // Check dither mask.
0395: if (maskData.length != maskDataByte.length) {
0396: return false;
0397: }
0398:
0399: for (int i = 0; i < maskData.length; i++) {
0400: if (maskData[i].length != maskDataCache[i].length)
0401: return false;
0402: byte[] refData = maskDataCache[i];
0403: byte[] data = maskData[i];
0404: for (int j = 0; j < maskData[i].length; j++) {
0405: if (data[j] != refData[j])
0406: return false;
0407: }
0408: }
0409:
0410: return true;
0411: }
0412: } // End inner class DitherLUT.
0413:
0414: /**
0415: * Initialize data type-dependent fields including the dither mask data
0416: * arrays and, for optimized byte cases, the dither lookup table object.
0417: *
0418: * @param dataType The data type as defined in <code>DataBuffer</code>.
0419: * @param ditherMask The dither mask represented as an array of
0420: * <code>KernelJAI</code> objects.
0421: */
0422: private void initializeDitherData(int dataType,
0423: KernelJAI[] ditherMask) {
0424: switch (dataType) {
0425: case DataBuffer.TYPE_BYTE: {
0426: maskDataByte = new byte[ditherMask.length][];
0427: for (int i = 0; i < maskDataByte.length; i++) {
0428: float[] maskData = ditherMask[i].getKernelData();
0429: maskDataByte[i] = new byte[maskData.length];
0430: for (int j = 0; j < maskData.length; j++) {
0431: maskDataByte[i][j] = (byte) ((int) (maskData[j] * 255.0F) & 0xff);
0432: }
0433: }
0434:
0435: initializeDitherLUT();
0436: }
0437: break;
0438:
0439: case DataBuffer.TYPE_SHORT:
0440: case DataBuffer.TYPE_USHORT: {
0441: int scaleFactor = (int) Short.MAX_VALUE
0442: - (int) Short.MIN_VALUE;
0443: maskDataInt = new int[ditherMask.length][];
0444: for (int i = 0; i < maskDataInt.length; i++) {
0445: float[] maskData = ditherMask[i].getKernelData();
0446: maskDataInt[i] = new int[maskData.length];
0447: for (int j = 0; j < maskData.length; j++) {
0448: maskDataInt[i][j] = (int) (maskData[j] * scaleFactor);
0449: }
0450: }
0451: }
0452: break;
0453:
0454: case DataBuffer.TYPE_INT: {
0455: long scaleFactor = (long) Integer.MAX_VALUE
0456: - (long) Integer.MIN_VALUE;
0457: maskDataLong = new long[ditherMask.length][];
0458: for (int i = 0; i < maskDataLong.length; i++) {
0459: float[] maskData = ditherMask[i].getKernelData();
0460: maskDataLong[i] = new long[maskData.length];
0461: for (int j = 0; j < maskData.length; j++) {
0462: maskDataLong[i][j] = (long) (maskData[j] * scaleFactor);
0463: }
0464: }
0465: }
0466: break;
0467:
0468: case DataBuffer.TYPE_FLOAT:
0469: case DataBuffer.TYPE_DOUBLE: {
0470: maskDataFloat = new float[ditherMask.length][];
0471: for (int i = 0; i < maskDataFloat.length; i++) {
0472: maskDataFloat[i] = ditherMask[i].getKernelData();
0473: }
0474: }
0475: break;
0476:
0477: default:
0478: throw new RuntimeException(JaiI18N
0479: .getString("OrderedDitherOpImage0"));
0480: }
0481: }
0482:
0483: /**
0484: * For byte data only, initialize the dither lookup table if it is small
0485: * enough and set the type of ordered dither implementation to use.
0486: */
0487: private synchronized void initializeDitherLUT() {
0488: // Check whether a DitherLUT may be used.
0489: if (numBands * maskHeight * maskWidth * 256 > DITHER_LUT_LENGTH_MAX) {
0490: odType = TYPE_OD_GENERAL; // NB: This is superfluous.
0491: return;
0492: }
0493:
0494: // If execution has proceeded to this point then this is one of the
0495: // optimized cases so set the type flag accordingly.
0496: odType = numBands == 3 ? TYPE_OD_BYTE_LUT_3BAND
0497: : TYPE_OD_BYTE_LUT_NBAND;
0498:
0499: // Check whether an equivalent DitherLUT object already exists.
0500: int index = 0;
0501: while (index < ditherLUTCache.size()) {
0502: SoftReference lutRef = (SoftReference) ditherLUTCache
0503: .get(index);
0504: DitherLUT lut = (DitherLUT) lutRef.get();
0505: if (lut == null) {
0506: // The reference has been cleared: remove the Vector element
0507: // but do not increment the loop index.
0508: ditherLUTCache.remove(index);
0509: } else {
0510: if (lut.equals(dims, mults, maskDataByte)) {
0511: // Found an equivalent DitherLUT so use it and exit loop.
0512: odLUT = lut;
0513: break;
0514: }
0515: // Move on to the next Vector element.
0516: index++;
0517: }
0518: }
0519:
0520: // Create a new DitherLUT if an equivalent one was not found.
0521: if (odLUT == null) {
0522: odLUT = new DitherLUT(dims, mults, maskDataByte);
0523: // Cache a reference to the DitherLUT if there is room.
0524: if (ditherLUTCache.size() < DITHER_LUT_CACHE_LENGTH_MAX) {
0525: ditherLUTCache.add(new SoftReference(odLUT));
0526: }
0527: }
0528: }
0529:
0530: /**
0531: * Computes a tile of the dithered destination image.
0532: *
0533: * @param sources Cobbled sources, guaranteed to provide all the
0534: * source data necessary for computing the rectangle.
0535: * @param dest The tile containing the rectangle to be computed.
0536: * @param destRect The rectangle within the tile to be computed.
0537: */
0538: protected void computeRect(Raster[] sources, WritableRaster dest,
0539: Rectangle destRect) {
0540: // Set format tags
0541: RasterFormatTag[] formatTags = null;
0542: if (ImageUtil.isBinary(getSampleModel())
0543: && !ImageUtil.isBinary(getSourceImage(0)
0544: .getSampleModel())) {
0545: // XXX Workaround for bug 4521097. This branch of the if-block
0546: // should be deleted once bug 4668327 is fixed.
0547: RenderedImage[] sourceArray = new RenderedImage[] { getSourceImage(0) };
0548: RasterFormatTag[] sourceTags = RasterAccessor
0549: .findCompatibleTags(sourceArray, sourceArray[0]);
0550: RasterFormatTag[] destTags = RasterAccessor
0551: .findCompatibleTags(sourceArray, this );
0552: formatTags = new RasterFormatTag[] { sourceTags[0],
0553: destTags[1] };
0554: } else {
0555: // Retrieve format tags.
0556: formatTags = getFormatTags();
0557: }
0558:
0559: RasterAccessor src = new RasterAccessor(sources[0], destRect,
0560: formatTags[0], getSource(0).getColorModel());
0561: RasterAccessor dst = new RasterAccessor(dest, destRect,
0562: formatTags[1], getColorModel());
0563:
0564: switch (src.getDataType()) {
0565: case DataBuffer.TYPE_BYTE:
0566: computeRectByte(src, dst);
0567: break;
0568: case DataBuffer.TYPE_SHORT:
0569: computeRectShort(src, dst);
0570: break;
0571: case DataBuffer.TYPE_USHORT:
0572: computeRectUShort(src, dst);
0573: break;
0574: case DataBuffer.TYPE_INT:
0575: computeRectInt(src, dst);
0576: break;
0577: case DataBuffer.TYPE_FLOAT:
0578: computeRectFloat(src, dst);
0579: break;
0580: case DataBuffer.TYPE_DOUBLE:
0581: computeRectDouble(src, dst);
0582: break;
0583: default:
0584: throw new RuntimeException(JaiI18N
0585: .getString("OrderedDitherOpImage1"));
0586: }
0587:
0588: dst.copyDataToRaster();
0589: }
0590:
0591: /**
0592: * Computes a <code>Rectangle</code> of data for byte imagery.
0593: */
0594: private void computeRectByte(RasterAccessor src, RasterAccessor dst) {
0595: int sbands = src.getNumBands();
0596: int sLineStride = src.getScanlineStride();
0597: int sPixelStride = src.getPixelStride();
0598: int[] sBandOffsets = src.getBandOffsets();
0599: byte[][] sData = src.getByteDataArrays();
0600:
0601: int dwidth = dst.getWidth();
0602: int dheight = dst.getHeight();
0603: int dLineStride = dst.getScanlineStride();
0604: int dPixelStride = dst.getPixelStride();
0605: int dBandOffset = dst.getBandOffset(0);
0606: byte[] dData = dst.getByteDataArray(0);
0607:
0608: int xMod = dst.getX() % maskWidth;
0609: int y0 = dst.getY();
0610:
0611: switch (odType) {
0612: case TYPE_OD_BYTE_LUT_3BAND:
0613: case TYPE_OD_BYTE_LUT_NBAND:
0614: int[] srcLineOffsets = (int[]) sBandOffsets.clone();
0615: int[] srcPixelOffsets = (int[]) srcLineOffsets.clone();
0616: int dLineOffset = dBandOffset;
0617:
0618: for (int h = 0; h < dheight; h++) {
0619: int yMod = (y0 + h) % maskHeight;
0620:
0621: if (odType == TYPE_OD_BYTE_LUT_3BAND) {
0622: computeLineByteLUT3(sData, srcPixelOffsets,
0623: sPixelStride, dData, dLineOffset,
0624: dPixelStride, dwidth, xMod, yMod);
0625: } else {
0626: computeLineByteLUTN(sData, srcPixelOffsets,
0627: sPixelStride, dData, dLineOffset,
0628: dPixelStride, dwidth, xMod, yMod);
0629: }
0630:
0631: for (int i = 0; i < sbands; i++) {
0632: srcLineOffsets[i] += sLineStride;
0633: srcPixelOffsets[i] = srcLineOffsets[i];
0634: }
0635: dLineOffset += dLineStride;
0636: }
0637:
0638: break;
0639: case TYPE_OD_GENERAL:
0640: default:
0641: computeRectByteGeneral(sData, sBandOffsets, sLineStride,
0642: sPixelStride, dData, dBandOffset, dLineStride,
0643: dPixelStride, dwidth, dheight, xMod, y0);
0644: }
0645: }
0646:
0647: /**
0648: * Dithers a line of 3-band byte data using a DitherLUT.
0649: */
0650: private void computeLineByteLUT3(byte[][] sData,
0651: int[] sPixelOffsets, int sPixelStride, byte[] dData,
0652: int dPixelOffset, int dPixelStride, int dwidth, int xMod,
0653: int yMod) {
0654: int ditherLUTBandStride = odLUT.ditherLUTBandStride;
0655: int ditherLUTRowStride = odLUT.ditherLUTRowStride;
0656: int ditherLUTColStride = odLUT.ditherLUTColStride;
0657: byte[] ditherLUT = odLUT.ditherLUT;
0658:
0659: int base = adjustedOffset;
0660:
0661: int dlut0 = yMod * ditherLUTRowStride;
0662: int dlut1 = dlut0 + ditherLUTBandStride;
0663: int dlut2 = dlut1 + ditherLUTBandStride;
0664:
0665: int dlutLimit = dlut0 + ditherLUTRowStride;
0666:
0667: int xDelta = xMod * ditherLUTColStride;
0668: int pDtab0 = dlut0 + xDelta;
0669: int pDtab1 = dlut1 + xDelta;
0670: int pDtab2 = dlut2 + xDelta;
0671:
0672: byte[] sData0 = sData[0];
0673: byte[] sData1 = sData[1];
0674: byte[] sData2 = sData[2];
0675:
0676: for (int count = dwidth; count > 0; count--) {
0677: int idx = (ditherLUT[pDtab0
0678: + (sData0[sPixelOffsets[0]] & 0xff)] & 0xff)
0679: + (ditherLUT[pDtab1
0680: + (sData1[sPixelOffsets[1]] & 0xff)] & 0xff)
0681: + (ditherLUT[pDtab2
0682: + (sData2[sPixelOffsets[2]] & 0xff)] & 0xff);
0683:
0684: dData[dPixelOffset] = (byte) ((idx + base) & 0xff);
0685:
0686: sPixelOffsets[0] += sPixelStride;
0687: sPixelOffsets[1] += sPixelStride;
0688: sPixelOffsets[2] += sPixelStride;
0689:
0690: dPixelOffset += dPixelStride;
0691:
0692: pDtab0 += ditherLUTColStride;
0693:
0694: if (pDtab0 >= dlutLimit) {
0695: pDtab0 = dlut0;
0696: pDtab1 = dlut1;
0697: pDtab2 = dlut2;
0698: } else {
0699: pDtab1 += ditherLUTColStride;
0700: pDtab2 += ditherLUTColStride;
0701: }
0702: }
0703: }
0704:
0705: /**
0706: * Dithers a line of N-band byte data using a DitherLUT.
0707: */
0708: private void computeLineByteLUTN(byte[][] sData,
0709: int[] sPixelOffsets, int sPixelStride, byte[] dData,
0710: int dPixelOffset, int dPixelStride, int dwidth, int xMod,
0711: int yMod) {
0712: int ditherLUTBandStride = odLUT.ditherLUTBandStride;
0713: int ditherLUTRowStride = odLUT.ditherLUTRowStride;
0714: int ditherLUTColStride = odLUT.ditherLUTColStride;
0715: byte[] ditherLUT = odLUT.ditherLUT;
0716:
0717: int base = adjustedOffset;
0718:
0719: int dlutRow = yMod * ditherLUTRowStride;
0720: int dlutCol = dlutRow + xMod * ditherLUTColStride;
0721: int dlutLimit = dlutRow + ditherLUTRowStride;
0722:
0723: for (int count = dwidth; count > 0; count--) {
0724: int dlutBand = dlutCol;
0725: int idx = base;
0726: for (int i = 0; i < numBands; i++) {
0727: idx += (ditherLUT[dlutBand
0728: + (sData[i][sPixelOffsets[i]] & 0xff)] & 0xff);
0729: dlutBand += ditherLUTBandStride;
0730: sPixelOffsets[i] += sPixelStride;
0731: }
0732:
0733: dData[dPixelOffset] = (byte) (idx & 0xff);
0734:
0735: dPixelOffset += dPixelStride;
0736:
0737: dlutCol += ditherLUTColStride;
0738:
0739: if (dlutCol >= dlutLimit) {
0740: dlutCol = dlutRow;
0741: }
0742: }
0743: }
0744:
0745: /**
0746: * Computes a <code>Rectangle</code> of data for byte imagery using the
0747: * general, unoptimized algorithm.
0748: */
0749: private void computeRectByteGeneral(byte[][] sData,
0750: int[] sBandOffsets, int sLineStride, int sPixelStride,
0751: byte[] dData, int dBandOffset, int dLineStride,
0752: int dPixelStride, int dwidth, int dheight, int xMod, int y0) {
0753: if (adjustedOffset > 0) {
0754: Arrays.fill(dData, (byte) (adjustedOffset & 0xff));
0755: }
0756:
0757: int sbands = sBandOffsets.length;
0758: for (int b = 0; b < sbands; b++) {
0759: byte[] s = sData[b];
0760: byte[] d = dData;
0761:
0762: byte[] maskData = maskDataByte[b];
0763:
0764: int sLineOffset = sBandOffsets[b];
0765: int dLineOffset = dBandOffset;
0766:
0767: for (int h = 0; h < dheight; h++) {
0768: int yMod = (y0 + h) % maskHeight;
0769:
0770: // Determine the index of the first dither mask point in
0771: // this line for the current band.
0772: int maskYBase = yMod * maskWidth;
0773:
0774: // Determine the value one greater than the maximum valid
0775: // dither mask index for this band.
0776: int maskLimit = maskYBase + maskWidth;
0777:
0778: // Initialize the dither mask index which is a value
0779: // guaranteed to be in range.
0780: int maskIndex = maskYBase + xMod;
0781:
0782: int sPixelOffset = sLineOffset;
0783: int dPixelOffset = dLineOffset;
0784:
0785: for (int w = 0; w < dwidth; w++) {
0786: int tmp = (s[sPixelOffset] & 0xff) * dims[b];
0787: int frac = (int) (tmp & 0xff);
0788: tmp >>= 8;
0789: if (frac > (int) (maskData[maskIndex] & 0xff)) {
0790: tmp++;
0791: }
0792:
0793: // Accumulate the value into the destination data array.
0794: int result = (d[dPixelOffset] & 0xff) + tmp
0795: * mults[b];
0796: d[dPixelOffset] = (byte) (result & 0xff);
0797:
0798: sPixelOffset += sPixelStride;
0799: dPixelOffset += dPixelStride;
0800:
0801: if (++maskIndex >= maskLimit) {
0802: maskIndex = maskYBase;
0803: }
0804: }
0805:
0806: sLineOffset += sLineStride;
0807: dLineOffset += dLineStride;
0808: }
0809: }
0810:
0811: if (adjustedOffset < 0) {
0812: // Shift the result by the adjusted offset of the color map.
0813: int length = dData.length;
0814: for (int i = 0; i < length; i++) {
0815: dData[i] = (byte) ((dData[i] & 0xff) + adjustedOffset);
0816: }
0817: }
0818: }
0819:
0820: /**
0821: * Computes a <code>Rectangle</code> of data for signed short imagery.
0822: */
0823: private void computeRectShort(RasterAccessor src, RasterAccessor dst) {
0824: int sbands = src.getNumBands();
0825: int sLineStride = src.getScanlineStride();
0826: int sPixelStride = src.getPixelStride();
0827: int[] sBandOffsets = src.getBandOffsets();
0828: short[][] sData = src.getShortDataArrays();
0829:
0830: int dwidth = dst.getWidth();
0831: int dheight = dst.getHeight();
0832: int dLineStride = dst.getScanlineStride();
0833: int dPixelStride = dst.getPixelStride();
0834: int dBandOffset = dst.getBandOffset(0);
0835: short[] dData = dst.getShortDataArray(0);
0836:
0837: // Initialize the destination data to the color cube adjusted offset
0838: // to permit accumulation of the result for each band.
0839: if (adjustedOffset != 0) {
0840: Arrays.fill(dData, (short) (adjustedOffset & 0xffff));
0841: }
0842:
0843: int xMod = dst.getX() % maskWidth;
0844: int y0 = dst.getY();
0845:
0846: for (int b = 0; b < sbands; b++) {
0847: short[] s = sData[b];
0848: short[] d = dData;
0849:
0850: int[] maskData = maskDataInt[b];
0851:
0852: int sLineOffset = sBandOffsets[b];
0853: int dLineOffset = dBandOffset;
0854:
0855: for (int h = 0; h < dheight; h++) {
0856: int sPixelOffset = sLineOffset;
0857: int dPixelOffset = dLineOffset;
0858:
0859: sLineOffset += sLineStride;
0860: dLineOffset += dLineStride;
0861:
0862: // Determine the index of the first dither mask point in
0863: // this line for the current band.
0864: int maskYBase = ((y0 + h) % maskHeight) * maskWidth;
0865:
0866: // Determine the value one greater than the maximum valid
0867: // dither mask index for this band.
0868: int maskLimit = maskYBase + maskWidth;
0869:
0870: // Initialize the dither mask index which is a value
0871: // guaranteed to be in range.
0872: int maskIndex = maskYBase + xMod;
0873:
0874: for (int w = 0; w < dwidth; w++) {
0875: int tmp = (s[sPixelOffset] - Short.MIN_VALUE)
0876: * dims[b];
0877: int frac = (int) (tmp & 0xffff);
0878:
0879: // Accumulate the value into the destination data array.
0880: int result = (int) (d[dPixelOffset] & 0xffff)
0881: + (tmp >> 16) * mults[b];
0882: if (frac > maskData[maskIndex]) {
0883: result += mults[b];
0884: }
0885: d[dPixelOffset] = (short) (result & 0xffff);
0886:
0887: sPixelOffset += sPixelStride;
0888: dPixelOffset += dPixelStride;
0889:
0890: if (++maskIndex >= maskLimit) {
0891: maskIndex = maskYBase;
0892: }
0893: }
0894: }
0895: }
0896: }
0897:
0898: /**
0899: * Computes a <code>Rectangle</code> of data for unsigned short imagery.
0900: */
0901: private void computeRectUShort(RasterAccessor src,
0902: RasterAccessor dst) {
0903: int sbands = src.getNumBands();
0904: int sLineStride = src.getScanlineStride();
0905: int sPixelStride = src.getPixelStride();
0906: int[] sBandOffsets = src.getBandOffsets();
0907: short[][] sData = src.getShortDataArrays();
0908:
0909: int dwidth = dst.getWidth();
0910: int dheight = dst.getHeight();
0911: int dLineStride = dst.getScanlineStride();
0912: int dPixelStride = dst.getPixelStride();
0913: int dBandOffset = dst.getBandOffset(0);
0914: short[] dData = dst.getShortDataArray(0);
0915:
0916: // Initialize the destination data to the color cube adjusted offset
0917: // to permit accumulation of the result for each band.
0918: if (adjustedOffset != 0) {
0919: Arrays.fill(dData, (short) (adjustedOffset & 0xffff));
0920: }
0921:
0922: int xMod = dst.getX() % maskWidth;
0923: int y0 = dst.getY();
0924:
0925: for (int b = 0; b < sbands; b++) {
0926: short[] s = sData[b];
0927: short[] d = dData;
0928:
0929: int[] maskData = maskDataInt[b];
0930:
0931: int sLineOffset = sBandOffsets[b];
0932: int dLineOffset = dBandOffset;
0933:
0934: for (int h = 0; h < dheight; h++) {
0935: int sPixelOffset = sLineOffset;
0936: int dPixelOffset = dLineOffset;
0937:
0938: sLineOffset += sLineStride;
0939: dLineOffset += dLineStride;
0940:
0941: // Determine the index of the first dither mask point in
0942: // this line for the current band.
0943: int maskYBase = ((y0 + h) % maskHeight) * maskWidth;
0944:
0945: // Determine the value one greater than the maximum valid
0946: // dither mask index for this band.
0947: int maskLimit = maskYBase + maskWidth;
0948:
0949: // Initialize the dither mask index which is a value
0950: // guaranteed to be in range.
0951: int maskIndex = maskYBase + xMod;
0952:
0953: for (int w = 0; w < dwidth; w++) {
0954: int tmp = (s[sPixelOffset] & 0xffff) * dims[b];
0955: int frac = (int) (tmp & 0xffff);
0956:
0957: // Accumulate the value into the destination data array.
0958: int result = (int) (d[dPixelOffset] & 0xffff)
0959: + (tmp >> 16) * mults[b];
0960: if (frac > maskData[maskIndex]) {
0961: result += mults[b];
0962: }
0963: d[dPixelOffset] = (short) (result & 0xffff);
0964:
0965: sPixelOffset += sPixelStride;
0966: dPixelOffset += dPixelStride;
0967:
0968: if (++maskIndex >= maskLimit) {
0969: maskIndex = maskYBase;
0970: }
0971: }
0972: }
0973: }
0974: }
0975:
0976: /**
0977: * Computes a <code>Rectangle</code> of data for integer imagery.
0978: */
0979: private void computeRectInt(RasterAccessor src, RasterAccessor dst) {
0980: int sbands = src.getNumBands();
0981: int sLineStride = src.getScanlineStride();
0982: int sPixelStride = src.getPixelStride();
0983: int[] sBandOffsets = src.getBandOffsets();
0984: int[][] sData = src.getIntDataArrays();
0985:
0986: int dwidth = dst.getWidth();
0987: int dheight = dst.getHeight();
0988: int dLineStride = dst.getScanlineStride();
0989: int dPixelStride = dst.getPixelStride();
0990: int dBandOffset = dst.getBandOffset(0);
0991: int[] dData = dst.getIntDataArray(0);
0992:
0993: // Initialize the destination data to the color cube adjusted offset
0994: // to permit accumulation of the result for each band.
0995: if (adjustedOffset != 0) {
0996: Arrays.fill(dData, adjustedOffset);
0997: }
0998:
0999: int xMod = dst.getX() % maskWidth;
1000: int y0 = dst.getY();
1001:
1002: for (int b = 0; b < sbands; b++) {
1003: int[] s = sData[b];
1004: int[] d = dData;
1005:
1006: long[] maskData = maskDataLong[b];
1007:
1008: int sLineOffset = sBandOffsets[b];
1009: int dLineOffset = dBandOffset;
1010:
1011: for (int h = 0; h < dheight; h++) {
1012: int sPixelOffset = sLineOffset;
1013: int dPixelOffset = dLineOffset;
1014:
1015: sLineOffset += sLineStride;
1016: dLineOffset += dLineStride;
1017:
1018: // Determine the index of the first dither mask point in
1019: // this line for the current band.
1020: int maskYBase = ((y0 + h) % maskHeight) * maskWidth;
1021:
1022: // Determine the value one greater than the maximum valid
1023: // dither mask index for this band.
1024: int maskLimit = maskYBase + maskWidth;
1025:
1026: // Initialize the dither mask index which is a value
1027: // guaranteed to be in range.
1028: int maskIndex = maskYBase + xMod;
1029:
1030: for (int w = 0; w < dwidth; w++) {
1031: long tmp = ((long) s[sPixelOffset] - (long) Integer.MIN_VALUE)
1032: * dims[b];
1033: long frac = (long) (tmp & 0xffffffff);
1034:
1035: // Accumulate the value into the destination data array.
1036: int result = d[dPixelOffset] + ((int) (tmp >> 32))
1037: * mults[b];
1038: if (frac > maskData[maskIndex]) {
1039: result += mults[b];
1040: }
1041: d[dPixelOffset] = result;
1042:
1043: sPixelOffset += sPixelStride;
1044: dPixelOffset += dPixelStride;
1045:
1046: if (++maskIndex >= maskLimit) {
1047: maskIndex = maskYBase;
1048: }
1049: }
1050: }
1051: }
1052: }
1053:
1054: /**
1055: * Computes a <code>Rectangle</code> of data for float imagery.
1056: */
1057: private void computeRectFloat(RasterAccessor src, RasterAccessor dst) {
1058: int sbands = src.getNumBands();
1059: int sLineStride = src.getScanlineStride();
1060: int sPixelStride = src.getPixelStride();
1061: int[] sBandOffsets = src.getBandOffsets();
1062: float[][] sData = src.getFloatDataArrays();
1063:
1064: int dwidth = dst.getWidth();
1065: int dheight = dst.getHeight();
1066: int dLineStride = dst.getScanlineStride();
1067: int dPixelStride = dst.getPixelStride();
1068: int dBandOffset = dst.getBandOffset(0);
1069: float[] dData = dst.getFloatDataArray(0);
1070:
1071: // Initialize the destination data to the color cube adjusted offset
1072: // to permit accumulation of the result for each band.
1073: if (adjustedOffset != 0) {
1074: Arrays.fill(dData, (float) adjustedOffset);
1075: }
1076:
1077: int xMod = dst.getX() % maskWidth;
1078: int y0 = dst.getY();
1079:
1080: for (int b = 0; b < sbands; b++) {
1081: float[] s = sData[b];
1082: float[] d = dData;
1083:
1084: float[] maskData = maskDataFloat[b];
1085:
1086: int sLineOffset = sBandOffsets[b];
1087: int dLineOffset = dBandOffset;
1088:
1089: for (int h = 0; h < dheight; h++) {
1090: int sPixelOffset = sLineOffset;
1091: int dPixelOffset = dLineOffset;
1092:
1093: sLineOffset += sLineStride;
1094: dLineOffset += dLineStride;
1095:
1096: // Determine the index of the first dither mask point in
1097: // this line for the current band.
1098: int maskYBase = ((y0 + h) % maskHeight) * maskWidth;
1099:
1100: // Determine the value one greater than the maximum valid
1101: // dither mask index for this band.
1102: int maskLimit = maskYBase + maskWidth;
1103:
1104: // Initialize the dither mask index which is a value
1105: // guaranteed to be in range.
1106: int maskIndex = maskYBase + xMod;
1107:
1108: for (int w = 0; w < dwidth; w++) {
1109: int tmp = (int) (s[sPixelOffset] * dims[b]);
1110: float frac = s[sPixelOffset] * dims[b] - tmp;
1111:
1112: // Accumulate the value into the destination data array.
1113: float result = d[dPixelOffset] + tmp * mults[b];
1114: if (frac > maskData[maskIndex]) {
1115: result += mults[b];
1116: }
1117: d[dPixelOffset] = result;
1118:
1119: sPixelOffset += sPixelStride;
1120: dPixelOffset += dPixelStride;
1121:
1122: if (++maskIndex >= maskLimit) {
1123: maskIndex = maskYBase;
1124: }
1125: }
1126: }
1127: }
1128: }
1129:
1130: /**
1131: * Computes a <code>Rectangle</code> of data for double imagery.
1132: */
1133: private void computeRectDouble(RasterAccessor src,
1134: RasterAccessor dst) {
1135: int sbands = src.getNumBands();
1136: int sLineStride = src.getScanlineStride();
1137: int sPixelStride = src.getPixelStride();
1138: int[] sBandOffsets = src.getBandOffsets();
1139: double[][] sData = src.getDoubleDataArrays();
1140:
1141: int dwidth = dst.getWidth();
1142: int dheight = dst.getHeight();
1143: int dLineStride = dst.getScanlineStride();
1144: int dPixelStride = dst.getPixelStride();
1145: int dBandOffset = dst.getBandOffset(0);
1146: double[] dData = dst.getDoubleDataArray(0);
1147:
1148: // Initialize the destination data to the color cube adjusted offset
1149: // to permit accumulation of the result for each band.
1150: if (adjustedOffset != 0) {
1151: Arrays.fill(dData, (double) adjustedOffset);
1152: }
1153:
1154: int xMod = dst.getX() % maskWidth;
1155: int y0 = dst.getY();
1156:
1157: for (int b = 0; b < sbands; b++) {
1158: double[] s = sData[b];
1159: double[] d = dData;
1160:
1161: float[] maskData = maskDataFloat[b];
1162:
1163: int sLineOffset = sBandOffsets[b];
1164: int dLineOffset = dBandOffset;
1165:
1166: for (int h = 0; h < dheight; h++) {
1167: int sPixelOffset = sLineOffset;
1168: int dPixelOffset = dLineOffset;
1169:
1170: sLineOffset += sLineStride;
1171: dLineOffset += dLineStride;
1172:
1173: // Determine the index of the first dither mask point in
1174: // this line for the current band.
1175: int maskYBase = ((y0 + h) % maskHeight) * maskWidth;
1176:
1177: // Determine the value one greater than the maximum valid
1178: // dither mask index for this band.
1179: int maskLimit = maskYBase + maskWidth;
1180:
1181: // Initialize the dither mask index which is a value
1182: // guaranteed to be in range.
1183: int maskIndex = maskYBase + xMod;
1184:
1185: for (int w = 0; w < dwidth; w++) {
1186: int tmp = (int) (s[sPixelOffset] * dims[b]);
1187: float frac = (float) (s[sPixelOffset] * dims[b] - tmp);
1188:
1189: // Accumulate the value into the destination data array.
1190: double result = d[dPixelOffset] + tmp * mults[b];
1191: if (frac > maskData[maskIndex]) {
1192: result += mults[b];
1193: }
1194: d[dPixelOffset] = result;
1195:
1196: sPixelOffset += sPixelStride;
1197: dPixelOffset += dPixelStride;
1198:
1199: if (++maskIndex >= maskLimit) {
1200: maskIndex = maskYBase;
1201: }
1202: }
1203: }
1204: }
1205: }
1206: }
|