001: package org.vfny.geoserver.wms.responses.palette;
002:
003: import java.awt.Point;
004: import java.awt.Rectangle;
005: import java.awt.RenderingHints;
006: import java.awt.Transparency;
007: import java.awt.geom.Point2D;
008: import java.awt.geom.Rectangle2D;
009: import java.awt.image.BufferedImage;
010: import java.awt.image.BufferedImageOp;
011: import java.awt.image.ColorModel;
012: import java.awt.image.IndexColorModel;
013: import java.awt.image.Raster;
014: import java.awt.image.RenderedImage;
015: import java.awt.image.WritableRaster;
016:
017: /**
018: * This class provide an Image oriented interface for the
019: * {@link EfficientInverseColorMapComputation}. Specifically, it is designed in
020: * order to implement the {@link BufferedImage} for processing
021: * {@link BufferedImage}s efficiently accessing the raster pixels directly but
022: * it also provide a method to process general {@link RenderedImage}s
023: * implementations.
024: *
025: * @author Simone Giannecchini - GeoSolutions SAS
026: * @see EfficientInverseColorMapComputation
027: *
028: */
029: public final class InverseColorMapOp implements BufferedImageOp {
030:
031: protected final InverseColorMapRasterOp rasterOp;
032:
033: protected final IndexColorModel icm;
034:
035: protected final int alphaThreshold;
036:
037: protected final boolean hasAlpha;
038:
039: protected final int transparencyIndex;
040:
041: public InverseColorMapOp(final IndexColorModel destCM,
042: final int quantizationColors, final int alphaThreshold) {
043: this .rasterOp = new InverseColorMapRasterOp(destCM,
044: quantizationColors, alphaThreshold);
045: this .icm = destCM;
046: this .alphaThreshold = alphaThreshold;
047: hasAlpha = icm.hasAlpha();
048: transparencyIndex = icm.getTransparentPixel();
049:
050: }
051:
052: public InverseColorMapOp(final IndexColorModel destCM) {
053: this (destCM,
054: InverseColorMapRasterOp.DEFAULT_QUANTIZATION_COLORS,
055: InverseColorMapRasterOp.DEFAULT_ALPHA_TH);
056: }
057:
058: public BufferedImage createCompatibleDestImage(BufferedImage src,
059: ColorModel destCM) {
060: if (!(destCM instanceof IndexColorModel)
061: || ((IndexColorModel) destCM).getTransparency() == Transparency.TRANSLUCENT)
062: return null;
063: return new BufferedImage(src.getWidth(), src.getHeight(),
064: BufferedImage.TYPE_BYTE_INDEXED,
065: (IndexColorModel) destCM);
066: }
067:
068: public BufferedImage filter(BufferedImage src, BufferedImage dest) {
069: if (dest == null)
070: dest = new BufferedImage(src.getWidth(), src.getHeight(),
071: BufferedImage.TYPE_BYTE_INDEXED, icm);
072: else {
073: if (!(dest.getColorModel() instanceof IndexColorModel)
074: || ((IndexColorModel) dest.getColorModel())
075: .getTransparency() != this .transparencyIndex)
076: throw new IllegalArgumentException();
077: if (((IndexColorModel) dest.getColorModel())
078: .getTransparentPixel() != this .transparencyIndex)
079: throw new IllegalArgumentException();
080: }
081: final WritableRaster wr = dest.getRaster();
082: final Raster ir = src.getRaster();
083: this .rasterOp.filter(ir, wr);
084: return dest;
085: }
086:
087: public BufferedImage filterRenderedImage(RenderedImage src) {
088: // //
089: //
090: // ShortCut for using bufferedimages and avoiding tiling
091: //
092: // //
093: if (src instanceof BufferedImage)
094: return filter((BufferedImage) src, null);
095:
096: // //
097: //
098: // Create the destination image
099: //
100: // //
101: final BufferedImage dest = new BufferedImage(src.getWidth(),
102: src.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, icm);
103: final WritableRaster destWr = dest.getRaster();
104:
105: // //
106: //
107: // Filter the image out
108: //
109: // //
110:
111: // /
112: //
113: // Optimize the hell out of this code. We have a single tile, let's go
114: // fast!
115: //
116: // //
117: if (src.getNumXTiles() == 1 && src.getNumYTiles() == 1) {
118: final int minTileX = src.getMinTileX();
119: final int minTileY = src.getMinTileY();
120: final Raster sourceR = src.getTile(minTileX, minTileY);
121: rasterOp.filter(sourceR.createChild(src.getMinX(), src
122: .getMinY(), src.getWidth(), src.getHeight(), 0, 0,
123: null), destWr);
124: return dest;
125: }
126:
127: // //
128: //
129: // Collecting info about the source image
130: //
131: // //
132: final int numBands = src.getSampleModel().getNumBands();
133: final int rgba[] = new int[numBands];
134: final boolean sourceHasAlpha = (numBands % 2 == 0);
135: final int alphaBand = sourceHasAlpha ? numBands - 1 : -1;
136: final EfficientInverseColorMapComputation invCM = rasterOp
137: .getInvCM();
138: final int minx_ = src.getMinX();
139: final int miny_ = src.getMinY();
140: final int srcW_ = src.getWidth();
141: final int srcH_ = src.getHeight();
142: final int maxx_ = minx_ + srcW_;
143: final int maxy_ = miny_ + srcH_;
144: final int minTileX = src.getMinTileX();
145: final int minTileY = src.getMinTileY();
146: final int tileW = src.getTileWidth();
147: final int tileH = src.getTileHeight();
148: final int maxTileX = minTileX + src.getNumXTiles();
149: final int maxTileY = minTileY + src.getNumYTiles();
150: int dstTempX = 0;
151: int dstTempY = 0;
152: for (int ty = minTileY; ty < maxTileY; ty++) {
153: dstTempX = 0;
154: int actualWidth = 0;
155: int actualHeight = 0;
156: for (int tx = minTileX; tx < maxTileX; tx++) {
157: // get the source raster
158: final Raster r = src.getTile(tx, ty);
159:
160: int minx = r.getMinX();
161: int miny = r.getMinY();
162: minx = minx < minx_ ? minx_ : minx;
163: miny = miny < miny_ ? miny_ : miny;
164: int maxx = minx + tileW;
165: int maxy = miny + tileH;
166: maxx = maxx > maxx_ ? maxx_ : maxx;
167: maxy = maxy > maxy_ ? maxy_ : maxy;
168: actualWidth = maxx - minx;
169: actualHeight = maxy - miny;
170: for (int j = miny, jj = dstTempY; j < maxy; j++, jj++) {
171: for (int i = minx, ii = dstTempX; i < maxx; i++, ii++) {
172: r.getPixel(i, j, rgba);
173:
174: // wr.setPixel(i, j, rgba);
175:
176: if (!sourceHasAlpha
177: || !hasAlpha
178: || (sourceHasAlpha && hasAlpha && rgba[alphaBand] >= this .alphaThreshold)) {
179: int val = invCM.getIndexNearest(
180: rgba[0] & 0xff, rgba[1] & 0xff,
181: rgba[2]);
182: if (hasAlpha && val >= transparencyIndex)
183: val++;
184: destWr.setSample(ii, jj, 0,
185: (byte) (val & 0xff));
186: } else
187: destWr.setSample(ii, jj, 0,
188: transparencyIndex);
189:
190: }
191: }
192: dstTempX += actualWidth;
193:
194: }
195: dstTempY += actualHeight;
196: }
197: return dest;
198: }
199:
200: public Rectangle2D getBounds2D(BufferedImage src) {
201: return new Rectangle(src.getWidth(), src.getHeight());
202: }
203:
204: public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
205: if (dstPt == null)
206: dstPt = new Point();
207: dstPt.setLocation(srcPt);
208: return dstPt;
209: }
210:
211: public RenderingHints getRenderingHints() {
212: return null;
213: }
214:
215: public IndexColorModel getIcm() {
216: return icm;
217: }
218:
219: }
|