001: /*
002: * $RCSfile: MlibErrorDiffusionOpImage.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:55:55 $
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.util.Map;
023: import javax.media.jai.ImageLayout;
024: import javax.media.jai.ColorCube;
025: import javax.media.jai.KernelJAI;
026: import javax.media.jai.LookupTableJAI;
027: import javax.media.jai.RasterFactory;
028: import javax.media.jai.UntiledOpImage;
029: import com.sun.media.jai.util.ImageUtil;
030: import com.sun.media.jai.util.JDKWorkarounds;
031: import com.sun.medialib.mlib.Image;
032: import com.sun.medialib.mlib.mediaLibImage;
033: import com.sun.medialib.mlib.mediaLibImageColormap;
034:
035: /**
036: * An OpImage class that performs an "ErrorDiffusion" operation.
037: *
038: */
039: final class MlibErrorDiffusionOpImage extends UntiledOpImage {
040: /**
041: * The integer-to-float scale factor for kernel elements.
042: */
043: private static final int KERNEL_SCALE_EXPONENT = 16;
044:
045: /**
046: * The mediaLib colormap.
047: */
048: protected mediaLibImageColormap mlibColormap;
049:
050: /**
051: * The scaled values of the error kernel in row major order.
052: */
053: protected int[] kernel;
054:
055: /**
056: * The width of the kernel.
057: */
058: protected int kernelWidth;
059:
060: /**
061: * The height of the kernel.
062: */
063: protected int kernelHeight;
064:
065: /**
066: * The X position of the key element in the kernel.
067: */
068: protected int kernelKeyX;
069:
070: /**
071: * The Y position of the key element in the kernel.
072: */
073: protected int kernelKeyY;
074:
075: /**
076: * Scale factor to convert kernel from floating point to integer.
077: */
078: protected int kernelScale;
079:
080: /**
081: * Force the destination image to be single-banded.
082: */
083: // Copied whole hog from ErrorDiffusionOpImage.
084: static ImageLayout layoutHelper(ImageLayout layout,
085: RenderedImage source, LookupTableJAI colormap) {
086: // Create or clone the layout.
087: ImageLayout il = layout == null ? new ImageLayout()
088: : (ImageLayout) layout.clone();
089:
090: // Force the destination and source origins and dimensions to coincide.
091: il.setMinX(source.getMinX());
092: il.setMinY(source.getMinY());
093: il.setWidth(source.getWidth());
094: il.setHeight(source.getHeight());
095:
096: // Get the SampleModel.
097: SampleModel sm = il.getSampleModel(source);
098:
099: // Ensure an appropriate SampleModel.
100: if (colormap.getNumBands() == 1
101: && colormap.getNumEntries() == 2
102: && !ImageUtil.isBinary(il.getSampleModel(source))) {
103: sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
104: il.getTileWidth(source), il.getTileHeight(source),
105: 1);
106: il.setSampleModel(sm);
107: }
108:
109: // Make sure that this OpImage is single-banded.
110: if (sm.getNumBands() != 1) {
111: sm = RasterFactory.createComponentSampleModel(sm, sm
112: .getTransferType(), sm.getWidth(), sm.getHeight(),
113: 1);
114: il.setSampleModel(sm);
115:
116: // Clear the ColorModel mask if needed.
117: ColorModel cm = il.getColorModel(null);
118: if (cm != null
119: && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
120: // Clear the mask bit if incompatible.
121: il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
122: }
123: }
124:
125: // Set an IndexColorModel on the image if:
126: // a. none is provided in the layout;
127: // b. source, destination, and colormap have byte data type;
128: // c. the colormap has 3 bands.
129: if ((layout == null || !il
130: .isValid(ImageLayout.COLOR_MODEL_MASK))
131: && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE
132: && sm.getDataType() == DataBuffer.TYPE_BYTE
133: && colormap.getDataType() == DataBuffer.TYPE_BYTE
134: && colormap.getNumBands() == 3) {
135: ColorModel cm = source.getColorModel();
136: if (cm == null
137: || (cm != null && cm.getColorSpace().isCS_sRGB())) {
138: int size = colormap.getNumEntries();
139: byte[][] cmap = new byte[3][256];
140: for (int i = 0; i < 3; i++) {
141: byte[] band = cmap[i];
142: byte[] data = colormap.getByteData(i);
143: int offset = colormap.getOffset(i);
144: int end = offset + size;
145: for (int j = 0; j < offset; j++) {
146: band[j] = (byte) 0;
147: }
148: for (int j = offset; j < end; j++) {
149: band[j] = data[j - offset];
150: }
151: for (int j = end; j < 256; j++) {
152: band[j] = (byte) 0xFF;
153: }
154: }
155:
156: il.setColorModel(new IndexColorModel(8, 256, cmap[0],
157: cmap[1], cmap[2]));
158: }
159: }
160:
161: return il;
162: }
163:
164: /**
165: * Constructs an MlibErrorDiffusionOpImage.
166: *
167: * @param source a RenderedImage.
168: * @param config configuration settings.
169: * @param layout an ImageLayout optionally containing the tile
170: * grid layout, SampleModel, and ColorModel, or null.
171: * @param colormap the target color quantization mapping.
172: * @param errorKernel the error diffusion coefficients.
173: */
174: public MlibErrorDiffusionOpImage(RenderedImage source, Map config,
175: ImageLayout layout, LookupTableJAI colormap,
176: KernelJAI errorKernel) {
177: // Pass the ImageLayout up directly as it was pre-proceseed
178: // in MlibErrorDiffusionRIF.
179: super (source, config, layout);
180:
181: // Initialize the mediaLib colormap. It is implicitly assumed that
182: // all data type and dimension checking was performed in the RIF.
183: this .mlibColormap = Image.ColorDitherInit(
184: colormap instanceof ColorCube ? ((ColorCube) colormap)
185: .getDimension() : null, Image.MLIB_BYTE,
186: ImageUtil.isBinary(sampleModel) ? Image.MLIB_BIT
187: : Image.MLIB_BYTE, colormap.getNumBands(),
188: colormap.getNumEntries(), colormap.getOffset(),
189: colormap.getByteData());
190:
191: // Initialize kernel constants.
192: this .kernelWidth = errorKernel.getWidth();
193: this .kernelHeight = errorKernel.getHeight();
194: this .kernelKeyX = errorKernel.getXOrigin();
195: this .kernelKeyY = errorKernel.getYOrigin();
196: this .kernelScale = 0x1 << KERNEL_SCALE_EXPONENT;
197:
198: // Initialize the integral kernel coefficients.
199: float[] kernelData = errorKernel.getKernelData();
200: int numElements = kernelData.length;
201: this .kernel = new int[numElements];
202: for (int i = 0; i < numElements; i++) {
203: kernel[i] = (int) (kernelData[i] * kernelScale);
204: }
205:
206: /* XXX
207: kernelWidth = kernelHeight = 3;
208: kernelKeyX = kernelKeyY = 1;
209: kernel = new int[] {0, 0, 0, 0, 0, 7, 3, 5, 1};
210: kernelScale = 4;
211: */
212: }
213:
214: /**
215: * Calculate the destination image from the source image.
216: *
217: * @param source The source Raster; should be the whole image.
218: * @param dest The destination WritableRaster; should be the whole image.
219: * @param destRect The destination Rectangle; should equal the destination
220: * image bounds.
221: */
222: protected void computeImage(Raster[] sources, WritableRaster dest,
223: Rectangle destRect) {
224:
225: Raster source = sources[0];
226:
227: int sourceFormatTag;
228: int destFormatTag;
229: if (ImageUtil.isBinary(dest.getSampleModel())) {
230: // Hack: derive the source format tag as if it was writing to
231: // a destination with the same layout as itself.
232: sourceFormatTag = MediaLibAccessor.findCompatibleTag(
233: sources, source);
234:
235: // Hard-code the destination format tag as we know that the image
236: // has a bilevel layout.
237: destFormatTag = dest.getSampleModel().getDataType()
238: | MediaLibAccessor.BINARY
239: | MediaLibAccessor.UNCOPIED;
240: } else {
241: sourceFormatTag = destFormatTag = MediaLibAccessor
242: .findCompatibleTag(sources, dest);
243: }
244:
245: MediaLibAccessor srcAccessor = new MediaLibAccessor(sources[0],
246: destRect, sourceFormatTag, false);
247: MediaLibAccessor dstAccessor = new MediaLibAccessor(dest,
248: destRect, destFormatTag, true);
249:
250: mediaLibImage[] srcML = srcAccessor.getMediaLibImages();
251: mediaLibImage[] dstML = dstAccessor.getMediaLibImages();
252:
253: Image.ColorErrorDiffusionMxN(dstML[0], srcML[0], kernel,
254: kernelWidth, kernelHeight, kernelKeyX, kernelKeyY,
255: KERNEL_SCALE_EXPONENT, mlibColormap);
256:
257: if (dstAccessor.isDataCopy()) {
258: dstAccessor.clampDataArrays();
259: dstAccessor.copyDataToRaster();
260: }
261: }
262: }
|