001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Oleg V. Khaschansky
019: * @version $Revision$
020: *
021: * @date: Sep 29, 2005
022: */package java.awt.image;
023:
024: import java.awt.*;
025: import java.awt.geom.Point2D;
026: import java.awt.geom.Rectangle2D;
027: import java.util.Arrays;
028:
029: import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor;
030: import org.apache.harmony.awt.internal.nls.Messages;
031:
032: public class ConvolveOp implements BufferedImageOp, RasterOp {
033:
034: public static final int EDGE_ZERO_FILL = 0;
035:
036: public static final int EDGE_NO_OP = 1;
037:
038: private Kernel kernel;
039: private int edgeCond;
040: private RenderingHints rhs = null;
041:
042: static {
043: // TODO
044: //System.loadLibrary("imageops");
045: }
046:
047: public ConvolveOp(Kernel kernel, int edgeCondition,
048: RenderingHints hints) {
049: this .kernel = kernel;
050: this .edgeCond = edgeCondition;
051: this .rhs = hints;
052: }
053:
054: public ConvolveOp(Kernel kernel) {
055: this .kernel = kernel;
056: this .edgeCond = EDGE_ZERO_FILL;
057: }
058:
059: public final Kernel getKernel() {
060: return (Kernel) kernel.clone();
061: }
062:
063: public final RenderingHints getRenderingHints() {
064: return rhs;
065: }
066:
067: public int getEdgeCondition() {
068: return edgeCond;
069: }
070:
071: public final Rectangle2D getBounds2D(Raster src) {
072: return src.getBounds();
073: }
074:
075: public final Rectangle2D getBounds2D(BufferedImage src) {
076: return getBounds2D(src.getRaster());
077: }
078:
079: public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
080: if (dstPt == null) {
081: dstPt = new Point2D.Float();
082: }
083:
084: dstPt.setLocation(srcPt);
085: return dstPt;
086: }
087:
088: public WritableRaster createCompatibleDestRaster(Raster src) {
089: return src.createCompatibleWritableRaster();
090: }
091:
092: public BufferedImage createCompatibleDestImage(BufferedImage src,
093: ColorModel dstCM) {
094: if (dstCM == null) {
095: dstCM = src.getColorModel();
096: }
097:
098: if (dstCM instanceof IndexColorModel) {
099: dstCM = ColorModel.getRGBdefault();
100: }
101:
102: WritableRaster r = dstCM.isCompatibleSampleModel(src
103: .getSampleModel()) ? src.getRaster()
104: .createCompatibleWritableRaster(src.getWidth(),
105: src.getHeight()) : dstCM
106: .createCompatibleWritableRaster(src.getWidth(), src
107: .getHeight());
108:
109: return new BufferedImage(dstCM, r,
110: dstCM.isAlphaPremultiplied(), null);
111: }
112:
113: public final WritableRaster filter(Raster src, WritableRaster dst) {
114: if (src == null) { // Should throw according to spec
115: // awt.256=Source raster is null
116: throw new NullPointerException(Messages
117: .getString("awt.256")); //$NON-NLS-1$
118: }
119:
120: if (src == dst) {
121: // awt.257=Source raster is equal to destination
122: throw new IllegalArgumentException(Messages
123: .getString("awt.257")); //$NON-NLS-1$
124: }
125:
126: if (dst == null) {
127: dst = createCompatibleDestRaster(src);
128: } else if (src.getNumBands() != dst.getNumBands()) {
129: // awt.258=Number of source bands ({0}) is not equal to number of destination bands ({1})
130: throw new IllegalArgumentException(Messages.getString(
131: "awt.258", src.getNumBands(), dst.getNumBands())); //$NON-NLS-1$
132: }
133:
134: // TODO
135: //if (ippFilter(src, dst, BufferedImage.TYPE_CUSTOM) != 0)
136: if (slowFilter(src, dst) != 0) {
137: // awt.21F=Unable to transform source
138: throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$
139: }
140:
141: return dst;
142: }
143:
144: private int slowFilter(Raster src, WritableRaster dst) {
145: try {
146: SampleModel sm = src.getSampleModel();
147:
148: int numBands = src.getNumBands();
149: int srcHeight = src.getHeight();
150: int srcWidth = src.getWidth();
151:
152: int xOrigin = kernel.getXOrigin();
153: int yOrigin = kernel.getYOrigin();
154: int kWidth = kernel.getWidth();
155: int kHeight = kernel.getHeight();
156: float[] data = kernel.getKernelData(null);
157:
158: int srcMinX = src.getMinX();
159: int srcMinY = src.getMinY();
160: int dstMinX = dst.getMinX();
161: int dstMinY = dst.getMinY();
162:
163: int srcConvMaxX = srcWidth - (kWidth - xOrigin - 1);
164: int srcConvMaxY = srcHeight - (kHeight - yOrigin - 1);
165:
166: int[] maxValues = new int[numBands];
167: int[] masks = new int[numBands];
168: int[] sampleSizes = sm.getSampleSize();
169:
170: for (int i = 0; i < numBands; i++) {
171: maxValues[i] = (1 << sampleSizes[i]) - 1;
172: masks[i] = ~(maxValues[i]);
173: }
174:
175: // Processing bounds
176: float[] pixels = null;
177: pixels = src.getPixels(srcMinX, srcMinY, srcWidth,
178: srcHeight, pixels);
179: float[] newPixels = new float[pixels.length];
180: int rowLength = srcWidth * numBands;
181: if (this .edgeCond == ConvolveOp.EDGE_NO_OP) {
182: // top
183: int start = 0;
184: int length = yOrigin * rowLength;
185: System.arraycopy(pixels, start, newPixels, start,
186: length);
187: // bottom
188: start = (srcHeight - (kHeight - yOrigin - 1))
189: * rowLength;
190: length = (kHeight - yOrigin - 1) * rowLength;
191: System.arraycopy(pixels, start, newPixels, start,
192: length);
193: // middle
194: length = xOrigin * numBands;
195: int length1 = (kWidth - xOrigin - 1) * numBands;
196: start = yOrigin * rowLength;
197: int start1 = (yOrigin + 1) * rowLength - length1;
198: for (int i = yOrigin; i < (srcHeight - (kHeight
199: - yOrigin - 1)); i++) {
200: System.arraycopy(pixels, start, newPixels, start,
201: length);
202: System.arraycopy(pixels, start1, newPixels, start1,
203: length1);
204: start += rowLength;
205: start1 += rowLength;
206: }
207:
208: }
209:
210: // Cycle over pixels to be calculated
211: for (int i = yOrigin; i < srcConvMaxY; i++) {
212: for (int j = xOrigin; j < srcConvMaxX; j++) {
213:
214: // Take kernel data in backward direction, convolution
215: int kernelIdx = data.length - 1;
216:
217: int pixelIndex = i * rowLength + j * numBands;
218: for (int hIdx = 0, rasterHIdx = i - yOrigin; hIdx < kHeight; hIdx++, rasterHIdx++) {
219: for (int wIdx = 0, rasterWIdx = j - xOrigin; wIdx < kWidth; wIdx++, rasterWIdx++) {
220: int curIndex = rasterHIdx * rowLength
221: + rasterWIdx * numBands;
222: for (int idx = 0; idx < numBands; idx++) {
223: newPixels[pixelIndex + idx] += data[kernelIdx]
224: * pixels[curIndex + idx];
225: }
226: kernelIdx--;
227: }
228: }
229:
230: // Check for overflow now
231: for (int idx = 0; idx < numBands; idx++) {
232: if (((int) newPixels[pixelIndex + idx] & masks[idx]) != 0) {
233: if (newPixels[pixelIndex + idx] < 0) {
234: newPixels[pixelIndex + idx] = 0;
235: } else {
236: newPixels[pixelIndex + idx] = maxValues[idx];
237: }
238: }
239: }
240: }
241: }
242:
243: dst.setPixels(dstMinX, dstMinY, srcWidth, srcHeight,
244: newPixels);
245: } catch (Exception e) { // Something goes wrong, signal error
246: return 1;
247: }
248: return 0;
249: }
250:
251: public final BufferedImage filter(BufferedImage src,
252: BufferedImage dst) {
253: if (src == null) {
254: // awt.259=Source image is null
255: throw new NullPointerException(Messages
256: .getString("awt.259")); //$NON-NLS-1$
257: }
258:
259: if (src == dst) {
260: // awt.25A=Source equals to destination
261: throw new IllegalArgumentException(Messages
262: .getString("awt.25A")); //$NON-NLS-1$
263: }
264:
265: ColorModel srcCM = src.getColorModel();
266: BufferedImage finalDst = null;
267:
268: if (srcCM instanceof IndexColorModel) {
269: src = ((IndexColorModel) srcCM).convertToIntDiscrete(src
270: .getRaster(), true);
271: srcCM = src.getColorModel();
272: }
273:
274: if (dst == null) {
275: dst = createCompatibleDestImage(src, srcCM);
276: } else {
277: if (!srcCM.equals(dst.getColorModel())) {
278: // Treat BufferedImage.TYPE_INT_RGB and BufferedImage.TYPE_INT_ARGB as same
279: if (!((src.getType() == BufferedImage.TYPE_INT_RGB || src
280: .getType() == BufferedImage.TYPE_INT_ARGB) && (dst
281: .getType() == BufferedImage.TYPE_INT_RGB || dst
282: .getType() == BufferedImage.TYPE_INT_ARGB))) {
283: finalDst = dst;
284: dst = createCompatibleDestImage(src, srcCM);
285: }
286: }
287: }
288:
289: // Skip alpha channel for TYPE_INT_RGB images
290: // TODO
291: //if (ippFilter(src.getRaster(), dst.getRaster(), src.getType()) != 0)
292: if (slowFilter(src.getRaster(), dst.getRaster()) != 0) {
293: // awt.21F=Unable to transform source
294: throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$
295: }
296:
297: if (finalDst != null) {
298: Graphics2D g = finalDst.createGraphics();
299: g.setComposite(AlphaComposite.Src);
300: g.drawImage(dst, 0, 0, null);
301: } else {
302: finalDst = dst;
303: }
304:
305: return finalDst;
306: }
307:
308: // TODO remove when this method is used
309: @SuppressWarnings("unused")
310: private int ippFilter(Raster src, WritableRaster dst, int imageType) {
311: int srcStride, dstStride;
312: boolean skipChannel = false;
313: int channels;
314: int offsets[] = null;
315:
316: switch (imageType) {
317: case BufferedImage.TYPE_INT_RGB:
318: case BufferedImage.TYPE_INT_BGR: {
319: channels = 4;
320: srcStride = src.getWidth() * 4;
321: dstStride = dst.getWidth() * 4;
322: skipChannel = true;
323: break;
324: }
325:
326: case BufferedImage.TYPE_INT_ARGB:
327: case BufferedImage.TYPE_INT_ARGB_PRE:
328: case BufferedImage.TYPE_4BYTE_ABGR:
329: case BufferedImage.TYPE_4BYTE_ABGR_PRE: {
330: channels = 4;
331: srcStride = src.getWidth() * 4;
332: dstStride = dst.getWidth() * 4;
333: break;
334: }
335:
336: case BufferedImage.TYPE_BYTE_GRAY: {
337: channels = 1;
338: srcStride = src.getWidth();
339: dstStride = dst.getWidth();
340: break;
341: }
342:
343: case BufferedImage.TYPE_3BYTE_BGR: {
344: channels = 3;
345: srcStride = src.getWidth() * 3;
346: dstStride = dst.getWidth() * 3;
347: break;
348: }
349:
350: case BufferedImage.TYPE_USHORT_GRAY: // TODO - could be done in native code?
351: case BufferedImage.TYPE_USHORT_565_RGB:
352: case BufferedImage.TYPE_USHORT_555_RGB:
353: case BufferedImage.TYPE_BYTE_BINARY: {
354: return slowFilter(src, dst);
355: }
356:
357: default: {
358: SampleModel srcSM = src.getSampleModel();
359: SampleModel dstSM = dst.getSampleModel();
360:
361: if (srcSM instanceof PixelInterleavedSampleModel
362: && dstSM instanceof PixelInterleavedSampleModel) {
363: // Check PixelInterleavedSampleModel
364: if (srcSM.getDataType() != DataBuffer.TYPE_BYTE
365: || dstSM.getDataType() != DataBuffer.TYPE_BYTE) {
366: return slowFilter(src, dst);
367: }
368:
369: channels = srcSM.getNumBands(); // Have IPP functions for 1, 3 and 4 channels
370: if (!(channels == 1 || channels == 3 || channels == 4)) {
371: return slowFilter(src, dst);
372: }
373:
374: srcStride = ((ComponentSampleModel) srcSM)
375: .getScanlineStride();
376: dstStride = ((ComponentSampleModel) dstSM)
377: .getScanlineStride();
378: } else if (srcSM instanceof SinglePixelPackedSampleModel
379: && dstSM instanceof SinglePixelPackedSampleModel) {
380: // Check SinglePixelPackedSampleModel
381: SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel) srcSM;
382: SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel) dstSM;
383:
384: channels = sppsm1.getNumBands();
385:
386: // TYPE_INT_RGB, TYPE_INT_ARGB...
387: if (sppsm1.getDataType() != DataBuffer.TYPE_INT
388: || sppsm2.getDataType() != DataBuffer.TYPE_INT
389: || !(channels == 3 || channels == 4)) {
390: return slowFilter(src, dst);
391: }
392:
393: // Check compatibility of sample models
394: if (!Arrays.equals(sppsm1.getBitOffsets(), sppsm2
395: .getBitOffsets())
396: || !Arrays.equals(sppsm1.getBitMasks(), sppsm2
397: .getBitMasks())) {
398: return slowFilter(src, dst);
399: }
400:
401: for (int i = 0; i < channels; i++) {
402: if (sppsm1.getSampleSize(i) != 8) {
403: return slowFilter(src, dst);
404: }
405: }
406:
407: if (channels == 3) { // Cannot skip channel, don't know which is alpha...
408: channels = 4;
409: }
410:
411: srcStride = sppsm1.getScanlineStride() * 4;
412: dstStride = sppsm2.getScanlineStride() * 4;
413: } else {
414: return slowFilter(src, dst);
415: }
416:
417: // Fill offsets if there's a child raster
418: if (src.getParent() != null || dst.getParent() != null) {
419: if (src.getSampleModelTranslateX() != 0
420: || src.getSampleModelTranslateY() != 0
421: || dst.getSampleModelTranslateX() != 0
422: || dst.getSampleModelTranslateY() != 0) {
423: offsets = new int[4];
424: offsets[0] = -src.getSampleModelTranslateX()
425: + src.getMinX();
426: offsets[1] = -src.getSampleModelTranslateY()
427: + src.getMinY();
428: offsets[2] = -dst.getSampleModelTranslateX()
429: + dst.getMinX();
430: offsets[3] = -dst.getSampleModelTranslateY()
431: + dst.getMinY();
432: }
433: }
434: }
435: }
436:
437: Object srcData, dstData;
438: AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor
439: .getInstance();
440: try {
441: srcData = dbAccess.getData(src.getDataBuffer());
442: dstData = dbAccess.getData(dst.getDataBuffer());
443: } catch (IllegalArgumentException e) {
444: return -1; // Unknown data buffer type
445: }
446:
447: return ippFilter32f(kernel.data, kernel.getWidth(), kernel
448: .getHeight(), kernel.getXOrigin(), kernel.getYOrigin(),
449: edgeCond, srcData, src.getWidth(), src.getHeight(),
450: srcStride, dstData, dst.getWidth(), dst.getHeight(),
451: dstStride, channels, skipChannel, offsets);
452: }
453:
454: private native int ippFilter32f(float kernel[], int kWidth,
455: int kHeight, int anchorX, int anchorY, int borderType,
456: Object src, int srcWidth, int srcHeight, int srcStride,
457: Object dst, int dstWidth, int dstHeight, int dstStride,
458: int channels, boolean skipChannel, int offsets[]);
459: }
|