001: /*
002: * $RCSfile: MlibOrderedDitherOpImage.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:56:03 $
010: * $State: Exp $
011: */package com.sun.media.jai.mlib;
012:
013: import java.awt.Rectangle;
014: import java.awt.image.ColorModel;
015: import java.awt.image.DataBuffer;
016: import java.awt.image.IndexColorModel;
017: import java.awt.image.MultiPixelPackedSampleModel;
018: import java.awt.image.Raster;
019: import java.awt.image.RenderedImage;
020: import java.awt.image.SampleModel;
021: import java.awt.image.WritableRaster;
022: import java.lang.ref.SoftReference;
023: import java.util.Arrays;
024: import java.util.Map;
025: import java.util.Vector;
026: import javax.media.jai.ColorCube;
027: import javax.media.jai.ImageLayout;
028: import javax.media.jai.KernelJAI;
029: import javax.media.jai.OpImage;
030: import javax.media.jai.PointOpImage;
031: import javax.media.jai.RasterAccessor;
032: import javax.media.jai.RasterFormatTag;
033: import javax.media.jai.RasterFactory;
034: import com.sun.media.jai.util.JDKWorkarounds;
035: import com.sun.media.jai.util.ImageUtil;
036: import com.sun.medialib.mlib.Image;
037: import com.sun.medialib.mlib.mediaLibImage;
038: import com.sun.medialib.mlib.mediaLibImageColormap;
039:
040: /**
041: * An OpImage that performs the OrderedDither operation on 1 image
042: * through mediaLib.
043: */
044: final class MlibOrderedDitherOpImage extends PointOpImage {
045:
046: /**
047: * The integer-to-float scale factor for dither mask elements.
048: */
049: private static final int DMASK_SCALE_EXPONENT = 16;
050:
051: /**
052: * The mediaLib colormap.
053: */
054: protected mediaLibImageColormap mlibColormap;
055:
056: /**
057: * The scaled values of the dither mask.
058: */
059: protected int[][] dmask;
060:
061: /**
062: * The width of the mask.
063: */
064: protected int dmaskWidth;
065:
066: /**
067: * The height of the mask.
068: */
069: protected int dmaskHeight;
070:
071: /**
072: * Scale factor to convert mask from floating point to integer.
073: */
074: protected int dmaskScale;
075:
076: /**
077: * Force the destination image to be single-banded.
078: */
079: static ImageLayout layoutHelper(ImageLayout layout,
080: RenderedImage source, ColorCube colormap) {
081: ImageLayout il;
082: if (layout == null) {
083: il = new ImageLayout(source);
084: } else {
085: il = (ImageLayout) layout.clone();
086: }
087:
088: // Get the SampleModel.
089: SampleModel sm = il.getSampleModel(source);
090:
091: // Ensure an appropriate SampleModel.
092: if (colormap.getNumBands() == 1
093: && colormap.getNumEntries() == 2
094: && !ImageUtil.isBinary(il.getSampleModel(source))) {
095: sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
096: il.getTileWidth(source), il.getTileHeight(source),
097: 1);
098: il.setSampleModel(sm);
099: }
100:
101: // Make sure that this OpImage is single-banded.
102: if (sm.getNumBands() != 1) {
103: // TODO: Force to SHORT or USHORT if FLOAT or DOUBLE?
104: sm = RasterFactory.createComponentSampleModel(sm, sm
105: .getTransferType(), sm.getWidth(), sm.getHeight(),
106: 1);
107: il.setSampleModel(sm);
108:
109: // Clear the ColorModel mask if needed.
110: ColorModel cm = il.getColorModel(null);
111: if (cm != null
112: && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
113: // Clear the mask bit if incompatible.
114: il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
115: }
116: }
117:
118: // Set an IndexColorModel on the image if:
119: // a. none is provided in the layout;
120: // b. source, destination, and colormap have byte data type;
121: // c. the colormap has 3 bands; and
122: // d. the source ColorModel is either null or is non-null
123: // and has a ColorSpace equal to CS_sRGB.
124: if ((layout == null || !il
125: .isValid(ImageLayout.COLOR_MODEL_MASK))
126: && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE
127: && il.getSampleModel(null).getDataType() == DataBuffer.TYPE_BYTE
128: && colormap.getDataType() == DataBuffer.TYPE_BYTE
129: && colormap.getNumBands() == 3) {
130: ColorModel cm = source.getColorModel();
131: if (cm == null
132: || (cm != null && cm.getColorSpace().isCS_sRGB())) {
133: int size = colormap.getNumEntries();
134: byte[][] cmap = new byte[3][256];
135: for (int i = 0; i < 3; i++) {
136: byte[] band = cmap[i];
137: byte[] data = colormap.getByteData(i);
138: int offset = colormap.getOffset(i);
139: int end = offset + size;
140: for (int j = 0; j < offset; j++) {
141: band[j] = (byte) 0;
142: }
143: for (int j = offset; j < end; j++) {
144: band[j] = data[j - offset];
145: }
146: for (int j = end; j < 256; j++) {
147: band[j] = (byte) 0xFF;
148: }
149: }
150:
151: il.setColorModel(new IndexColorModel(8, 256, cmap[0],
152: cmap[1], cmap[2]));
153: }
154: }
155:
156: return il;
157: }
158:
159: /**
160: * Constructs an MlibOrderedDitherOpImage. The image dimensions are copied
161: * from the source image. The tile grid layout, SampleModel, and
162: * ColorModel may optionally be specified by an ImageLayout object.
163: *
164: * @param source a RenderedImage.
165: * @param layout an ImageLayout optionally containing the tile
166: * grid layout, SampleModel, and ColorModel, or null.
167: */
168: public MlibOrderedDitherOpImage(RenderedImage source, Map config,
169: ImageLayout layout, ColorCube colormap,
170: KernelJAI[] ditherMask) {
171: // Construct as a PointOpImage.
172: super (source, layoutHelper(layout, source, colormap), config,
173: true);
174:
175: // Initialize the mediaLib colormap. It is implicitly assumed that
176: // all data type and dimension checking was performed in the RIF.
177: this .mlibColormap = Image.ColorDitherInit(colormap
178: .getDimension(), Image.MLIB_BYTE, ImageUtil
179: .isBinary(sampleModel) ? Image.MLIB_BIT
180: : Image.MLIB_BYTE, colormap.getNumBands(), colormap
181: .getNumEntries(), colormap.getOffset(), colormap
182: .getByteData());
183:
184: // Initialize dither mask constants.
185: this .dmaskWidth = ditherMask[0].getWidth();
186: this .dmaskHeight = ditherMask[0].getHeight();
187: this .dmaskScale = 0x1 << DMASK_SCALE_EXPONENT;
188:
189: int numMasks = ditherMask.length;
190: this .dmask = new int[numMasks][];
191:
192: for (int k = 0; k < numMasks; k++) {
193: KernelJAI mask = ditherMask[k];
194:
195: if (mask.getWidth() != dmaskWidth
196: || mask.getHeight() != dmaskHeight) {
197: throw new IllegalArgumentException(JaiI18N
198: .getString("MlibOrderedDitherOpImage0"));
199: }
200:
201: // Initialize the integral dither mask coefficients.
202: float[] dmaskData = ditherMask[k].getKernelData();
203: int numElements = dmaskData.length;
204: this .dmask[k] = new int[numElements];
205: int[] dm = this .dmask[k];
206: for (int i = 0; i < numElements; i++) {
207: dm[i] = (int) (dmaskData[i] * dmaskScale);
208: }
209: }
210:
211: // Set flag to permit in-place operation.
212: permitInPlaceOperation();
213: }
214:
215: /**
216: * OrderedDither the pixel values of a rectangle from the source.
217: * The source is cobbled.
218: *
219: * @param sources an array of sources, guarantee to provide all
220: * necessary source data for computing the rectangle.
221: * @param dest a tile that contains the rectangle to be computed.
222: * @param destRect the rectangle within this OpImage to be processed.
223: */
224: protected void computeRect(Raster[] sources, WritableRaster dest,
225: Rectangle destRect) {
226:
227: Raster source = sources[0];
228:
229: int sourceFormatTag;
230: int destFormatTag;
231: if (ImageUtil.isBinary(dest.getSampleModel())) {
232: // Hack: derive the source format tag as if it was writing to
233: // a destination with the same layout as itself.
234: sourceFormatTag = MediaLibAccessor.findCompatibleTag(
235: sources, source);
236:
237: // Hard-code the destination format tag as we know that the image
238: // has a bilevel layout.
239: destFormatTag = dest.getSampleModel().getDataType()
240: | MediaLibAccessor.BINARY
241: | MediaLibAccessor.UNCOPIED;
242: } else {
243: sourceFormatTag = destFormatTag = MediaLibAccessor
244: .findCompatibleTag(sources, dest);
245: }
246:
247: MediaLibAccessor srcAccessor = new MediaLibAccessor(sources[0],
248: destRect, sourceFormatTag, false);
249: MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,
250: destRect, destFormatTag, true);
251:
252: mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
253: mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
254:
255: Image.ColorOrderedDitherMxN(dstML[0], srcML[0], dmask,
256: dmaskWidth, dmaskHeight, DMASK_SCALE_EXPONENT,
257: mlibColormap);
258:
259: if (dstAccessor.isDataCopy()) {
260: dstAccessor.clampDataArrays();
261: dstAccessor.copyDataToRaster();
262: }
263: }
264: }
|