You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cf...@apache.org on 2012/10/25 21:01:49 UTC
svn commit: r1402274 [28/31] - in
/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext:
./ awt/ awt/color/ awt/font/ awt/g2d/ awt/geom/ awt/image/ awt/image/codec/
awt/image/codec/jpeg/ awt/image/codec/p...
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MorphologyOp.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MorphologyOp.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MorphologyOp.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MorphologyOp.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,1665 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ */
+package org.apache.flex.forks.batik.ext.awt.image.rendered;
+
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.color.ColorSpace;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.BufferedImageOp;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DirectColorModel;
+import java.awt.image.Raster;
+import java.awt.image.RasterOp;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.flex.forks.batik.ext.awt.image.GraphicsUtil;
+
+/**
+ * This class provides an implementation for the SVG
+ * feMorphology filter, as defined in Chapter 15, section 20
+ * of the SVG specification.
+ *
+ * @author <a href="mailto:sheng.pei@sun.com">Sheng Pei</a>
+ * @version $Id: MorphologyOp.java 489226 2006-12-21 00:05:36Z cam $
+ */
+public class MorphologyOp implements BufferedImageOp, RasterOp {
+ /**
+ * The radius of the operation on X axis
+ */
+ private int radiusX;
+ /**
+ * The radius of the operation on Y axis
+ */
+ private int radiusY;
+ /*
+ * Determine whether to do the dilation or erosion operation.
+ * Will do dilation when it's true and erosion when it's false.
+ */
+ private boolean doDilation;
+
+ /*
+ * rangeX is 2*radiusX+1, which is the width of the Kernel
+ */
+ private final int rangeX;
+
+ /*
+ * rangeY is 2*radiusY+1, which is the height of the Kernel
+ */
+ private final int rangeY;
+
+ /*
+ * sRGB ColorSpace instance used for compatibility checking
+ */
+ private final ColorSpace sRGB = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+
+ /*
+ * Linear RGB ColorSpace instance used for compatibility checking
+ */
+ private final ColorSpace lRGB = ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB);
+
+ /**
+ * @param radiusX defines the radius of filter operation on X-axis. Should not be negative.
+ * A value of zero will disable the effect of the operation on X-axis, as described
+ * in the SVG specification.
+ * @param radiusY defines the radius of filter operation on Y-axis. Should not be negative.
+ * A value of zero will disable the effect of the operation on Y-axis, as described
+ * in the SVG specification.
+ * @param doDilation defines whether to do dilation or erosion operation. Will do dilation
+ * when the value is true, erosion when false.
+ */
+ public MorphologyOp (int radiusX, int radiusY, boolean doDilation){
+ if (radiusX<=0 || radiusY<=0){
+ throw new IllegalArgumentException( "The radius of X-axis or Y-axis should not be Zero or Negatives." );
+ }
+ else {
+ this.radiusX = radiusX;
+ this.radiusY = radiusY;
+ this.doDilation = doDilation;
+ rangeX = 2*radiusX + 1;
+ rangeY = 2*radiusY + 1;
+ }
+ }
+
+ public Rectangle2D getBounds2D(Raster src){
+ checkCompatible(src.getSampleModel());
+ return new Rectangle(src.getMinX(), src.getMinY(), src.getWidth(), src.getHeight());
+ }
+
+ public Rectangle2D getBounds2D(BufferedImage src){
+ return new Rectangle(0, 0, src.getWidth(), src.getHeight());
+ }
+
+ public Point2D getPoint2D(Point2D srcPt, Point2D destPt){
+ // This operation does not affect pixel location
+ if(destPt==null)
+ destPt = new Point2D.Float();
+ destPt.setLocation(srcPt.getX(), srcPt.getY());
+ return destPt;
+ }
+
+ private void checkCompatible(ColorModel colorModel,
+ SampleModel sampleModel){
+ ColorSpace cs = colorModel.getColorSpace();
+
+ // Check that model is sRGB or linear RGB
+ if((!cs .equals (sRGB)) && (!cs .equals( lRGB)))
+ throw new IllegalArgumentException("Expected CS_sRGB or CS_LINEAR_RGB color model");
+
+ // Check ColorModel is of type DirectColorModel
+ if(!(colorModel instanceof DirectColorModel))
+ throw new IllegalArgumentException("colorModel should be an instance of DirectColorModel");
+
+ // Check transfer type
+ if(sampleModel.getDataType() != DataBuffer.TYPE_INT)
+ throw new IllegalArgumentException("colorModel's transferType should be DataBuffer.TYPE_INT");
+
+ // Check red, green, blue and alpha mask
+ DirectColorModel dcm = (DirectColorModel)colorModel;
+ if(dcm.getRedMask() != 0x00ff0000)
+ throw new IllegalArgumentException("red mask in source should be 0x00ff0000");
+ if(dcm.getGreenMask() != 0x0000ff00)
+ throw new IllegalArgumentException("green mask in source should be 0x0000ff00");
+ if(dcm.getBlueMask() != 0x000000ff)
+ throw new IllegalArgumentException("blue mask in source should be 0x000000ff");
+ if(dcm.getAlphaMask() != 0xff000000)
+ throw new IllegalArgumentException("alpha mask in source should be 0xff000000");
+ }
+
+ private boolean isCompatible(ColorModel colorModel,
+ SampleModel sampleModel){
+ ColorSpace cs = colorModel.getColorSpace();
+ // Check that model is sRGB or linear RGB
+ if((cs != ColorSpace.getInstance(ColorSpace.CS_sRGB))
+ &&
+ (cs != ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB)))
+ return false;
+
+ // Check ColorModel is of type DirectColorModel
+ if(!(colorModel instanceof DirectColorModel))
+ return false;
+
+ // Check transfer type
+ if(sampleModel.getDataType() != DataBuffer.TYPE_INT)
+ return false;
+
+ // Check red, green, blue and alpha mask
+ DirectColorModel dcm = (DirectColorModel)colorModel;
+ if(dcm.getRedMask() != 0x00ff0000)
+ return false;
+ if(dcm.getGreenMask() != 0x0000ff00)
+ return false;
+ if(dcm.getBlueMask() != 0x000000ff)
+ return false;
+ if(dcm.getAlphaMask() != 0xff000000)
+ return false;
+ return true;
+ }
+
+ private void checkCompatible(SampleModel model){
+ // Check model is ok: should be SinglePixelPackedSampleModel
+ if(!(model instanceof SinglePixelPackedSampleModel))
+ throw new IllegalArgumentException
+ ("MorphologyOp only works with Rasters " +
+ "using SinglePixelPackedSampleModels");
+ // Check number of bands
+ int nBands = model.getNumBands();
+ if(nBands!=4)
+ throw new IllegalArgumentException
+ ("MorphologyOp only words with Rasters having 4 bands");
+ // Check that integer packed.
+ if(model.getDataType()!=DataBuffer.TYPE_INT)
+ throw new IllegalArgumentException
+ ("MorphologyOp only works with Rasters using DataBufferInt");
+
+ // Check bit masks
+ int[] bitOffsets=((SinglePixelPackedSampleModel)model).getBitOffsets();
+ for(int i=0; i<bitOffsets.length; i++){
+ if(bitOffsets[i]%8 != 0)
+ throw new IllegalArgumentException
+ ("MorphologyOp only works with Rasters using 8 bits " +
+ "per band : " + i + " : " + bitOffsets[i]);
+ }
+ }
+
+ public RenderingHints getRenderingHints(){
+ return null;
+ }
+
+ public WritableRaster createCompatibleDestRaster(Raster src){
+ checkCompatible(src.getSampleModel());
+ // Src Raster is OK: create a similar Raster for destination.
+ return src.createCompatibleWritableRaster();
+ }
+
+ public BufferedImage createCompatibleDestImage(BufferedImage src,
+ ColorModel destCM){
+ BufferedImage dest = null;
+ if(destCM==null)
+ destCM = src.getColorModel();
+
+ WritableRaster wr;
+ wr = destCM.createCompatibleWritableRaster(src.getWidth(),
+ src.getHeight());
+ checkCompatible(destCM, wr.getSampleModel());
+
+ dest = new BufferedImage(destCM, wr,
+ destCM.isAlphaPremultiplied(), null);
+ return dest;
+ }
+
+ /*
+ * This method compares the two input variables according
+ * to the doDilation boolean variable.
+ */
+ static final boolean isBetter (final int v1, final int v2, final boolean doDilation) {
+ if (v1 > v2)
+ return doDilation;
+ if (v1 < v2)
+ return !doDilation;
+ return true;
+ }
+
+ /*
+ * This method deals with the condition that the Kernel is wider than
+ * the Image
+ */
+ private void specialProcessRow(Raster src, WritableRaster dest){
+ final int w = src.getWidth();
+ final int h = src.getHeight();
+
+ // Access the integer buffer for each image.
+ DataBufferInt srcDB = (DataBufferInt)src.getDataBuffer();
+ DataBufferInt dstDB = (DataBufferInt)dest.getDataBuffer();
+
+ // Offset defines where in the stack the real data begin
+ SinglePixelPackedSampleModel sppsm;
+ sppsm = (SinglePixelPackedSampleModel)src.getSampleModel();
+
+ final int srcOff = srcDB.getOffset() +
+ sppsm.getOffset(src.getMinX() - src.getSampleModelTranslateX(),
+ src.getMinY() - src.getSampleModelTranslateY());
+
+
+ sppsm = (SinglePixelPackedSampleModel)dest.getSampleModel();
+ final int dstOff = dstDB.getOffset() +
+ sppsm.getOffset(dest.getMinX() - dest.getSampleModelTranslateX(),
+ dest.getMinY() - dest.getSampleModelTranslateY());
+
+ // Stride is the distance between two consecutive column elements,
+ // in the one-dimention dataBuffer
+ final int srcScanStride = ((SinglePixelPackedSampleModel)src.getSampleModel()).getScanlineStride();
+ final int dstScanStride = ((SinglePixelPackedSampleModel)dest.getSampleModel()).getScanlineStride();
+
+ // Access the pixel value array
+ final int[] srcPixels = srcDB.getBankData()[0];
+ final int[] destPixels = dstDB.getBankData()[0];
+
+ // The pointer of src and dest indicating where the pixel values are
+ int sp, dp;
+
+ // Declaration for the circular buffer's implementation
+ // These are the circular buffers' head pointer and
+ // the index pointers
+
+ // bufferHead points to the leftmost element in the circular buffer
+ int bufferHead;
+
+ int maxIndexA;
+ int maxIndexR;
+ int maxIndexG;
+ int maxIndexB;
+
+ // Temp variables
+ int pel, currentPixel, lastPixel;
+ int a,r,g,b;
+ int a1,r1,g1,b1;
+
+ // If image width is less than or equal to the radiusX,
+ // all the pixels share the same max/min value
+ if (w<=radiusX){
+ for (int i=0; i<h; i++){
+ // pointing to the first pixels of each row
+ sp = srcOff + i*srcScanStride;
+ dp = dstOff + i*dstScanStride;
+ pel = srcPixels[sp++];
+ a = pel>>>24;
+ r = pel&0xff0000;
+ g = pel&0xff00;
+ b = pel&0xff;
+
+ for (int k=1; k<w; k++){
+ currentPixel = srcPixels[sp++];
+ a1 = currentPixel>>>24;
+ r1 = currentPixel&0xff0000;
+ g1 = currentPixel&0xff00;
+ b1 = currentPixel&0xff;
+
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ }
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ }
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ }
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ }
+ }
+ // all the element share the same max/min value
+ for (int k=0; k<w; k++){
+ destPixels[dp++] = (a << 24) | r | g | b;
+ }
+ }
+ }
+
+ // When radiusX < w <= 2*radiusX
+ else {
+
+ // The width of the circular buffer is w
+ final int [] bufferA = new int [w];
+ final int [] bufferR = new int [w];
+ final int [] bufferG = new int [w];
+ final int [] bufferB = new int [w];
+
+ for (int i=0; i<h; i++){
+ // initialization of pointers, indice
+ // at the head of each row
+ sp = srcOff + i*srcScanStride;
+ dp = dstOff + i*dstScanStride;
+
+ bufferHead = 0;
+ maxIndexA = 0;
+ maxIndexR = 0;
+ maxIndexG = 0;
+ maxIndexB = 0;
+
+ pel = srcPixels[sp++];
+ a = pel>>>24;
+ r = pel&0xff0000;
+ g = pel&0xff00;
+ b = pel&0xff;
+ bufferA[0] = a;
+ bufferR[0] = r;
+ bufferG[0] = g;
+ bufferB[0] = b;
+
+ for (int k=1; k<=radiusX; k++){
+ currentPixel = srcPixels[sp++];
+ a1 = currentPixel>>>24;
+ r1 = currentPixel&0xff0000;
+ g1 = currentPixel&0xff00;
+ b1 = currentPixel&0xff;
+ bufferA[k] = a1;
+ bufferR[k] = r1;
+ bufferG[k] = g1;
+ bufferB[k] = b1;
+
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = k;
+ }
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = k;
+ }
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = k;
+ }
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = k;
+ }
+ }
+ destPixels[dp++] = (a << 24) | r | g | b;
+
+ //
+ // 1 <= j <= w-radiusX-1 : The left margin of each row.
+ //
+ for (int j=1; j<=w-radiusX-1; j++){
+ lastPixel = srcPixels[sp++];
+
+ // here is the Alpha channel
+
+ // we retrieve the previous max/min value
+ a = bufferA[maxIndexA];
+ a1 = lastPixel>>>24;
+ bufferA[j+radiusX] = a1;
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = j+radiusX;
+ }
+
+ // now we deal with the Red channel
+
+ r = bufferR[maxIndexR];
+ r1 = lastPixel&0xff0000;
+ bufferR[j+radiusX] = r1;
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = j+radiusX;
+ }
+
+ // now we deal with the Green channel
+
+ g = bufferG[maxIndexG];
+ g1 = lastPixel&0xff00;
+ bufferG[j+radiusX] = g1;
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = j+radiusX;
+ }
+
+ // now we deal with the Blue channel
+
+ b = bufferB[maxIndexB];
+ b1 = lastPixel&0xff;
+ bufferB[j+radiusX] = b1;
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = j+radiusX;
+ }
+ // now we have gone through the four channels and
+ // updated the index array. then we'll pack the
+ // new max/min value according to each channel's
+ // max/min vlue
+
+ destPixels[dp++] = (a << 24) | r | g | b;
+ }
+ // Now is the inner body of the row:
+ // all elements in this segment share the same max/min value
+ for (int j = w-radiusX; j<= radiusX; j++){
+ destPixels[dp] = destPixels[dp-1];
+ dp++;
+ }
+ // Now the circular buffer is full
+ // Now is the right margin of the row when radiusX < w <= 2*radiusX
+ for (int j = radiusX+1; j<w; j++){
+
+ if (maxIndexA == bufferHead){
+ a = bufferA[bufferHead+1];
+ maxIndexA = bufferHead+1;
+ for (int m= bufferHead+2; m< w; m++){
+ a1 = bufferA[m];
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = m;
+ }
+ }
+ }
+ else {
+ a = bufferA[maxIndexA];
+ }
+ if (maxIndexR == bufferHead){
+ r = bufferR[bufferHead+1];
+ maxIndexR = bufferHead+1;
+ for (int m= bufferHead+2; m< w; m++){
+ r1 = bufferR[m];
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = m;
+ }
+ }
+ }
+ else {
+ r = bufferR[maxIndexR];
+ }
+
+ if (maxIndexG == bufferHead){
+ g = bufferG[bufferHead+1];
+ maxIndexG = bufferHead+1;
+ for (int m= bufferHead+2; m< w; m++){
+ g1 = bufferG[m];
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ g = bufferG[maxIndexG];
+ }
+
+ if (maxIndexB == bufferHead){
+ b = bufferB[bufferHead+1];
+ maxIndexB = bufferHead+1;
+ for (int m= bufferHead+2; m< w; m++){
+ b1 = bufferB[m];
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ b = bufferB[maxIndexB];
+ }
+
+ // discard the leftmost element
+ bufferHead++;
+
+ destPixels[dp++] = (a << 24) | r | g | b;
+ }
+ // return to the first pixel of the next row
+ }
+ }// When radiusX < w <=2*radiusX
+ }
+
+ /*
+ * This method deals with the condition when the Kernel is
+ * higher than the image.
+ */
+ private void specialProcessColumn(Raster src, WritableRaster dest){
+
+ final int w = src.getWidth();
+ final int h = src.getHeight();
+
+ // Access the integer buffer for each image.
+ DataBufferInt dstDB = (DataBufferInt)dest.getDataBuffer();
+
+ // Offset defines where in the stack the real data begin
+ final int dstOff = dstDB.getOffset();
+
+ // Stride is the distance between two consecutive column elements,
+ // in the one-dimention dataBuffer
+ final int dstScanStride = ((SinglePixelPackedSampleModel)dest.getSampleModel()).getScanlineStride();
+
+ // Access the pixel value array
+ final int[] destPixels = dstDB.getBankData()[0];
+
+ // The pointer of src and dest indicating where the pixel values are
+ int dp, cp;
+
+ // Declaration for the circular buffer's implementation
+ // These are the circular buffers' head pointer and
+ // the index pointers
+
+ // bufferHead points to the leftmost element in the circular buffer
+ int bufferHead;
+
+ int maxIndexA;
+ int maxIndexR;
+ int maxIndexG;
+ int maxIndexB;
+
+ // Temp variables
+ int pel, currentPixel, lastPixel;
+ int a,r,g,b;
+ int a1,r1,g1,b1;
+
+ // Here all the pixels share the same
+ // max/min value
+ if (h<=radiusY){
+ for (int j=0; j<w; j++){
+ dp = dstOff + j;
+ cp = dstOff + j;
+ pel = destPixels[cp];
+ cp += dstScanStride;
+ a = pel>>>24;
+ r = pel&0xff0000;
+ g = pel&0xff00;
+ b = pel&0xff;
+
+ for (int k=1; k<h; k++){
+ currentPixel = destPixels[cp];
+ cp += dstScanStride;
+ a1 = currentPixel>>>24;
+ r1 = currentPixel&0xff0000;
+ g1 = currentPixel&0xff00;
+ b1 = currentPixel&0xff;
+
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ }
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ }
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ }
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ }
+ }
+ for (int k=0; k<h; k++){
+ destPixels[dp] = (a << 24) | r | g | b;
+ dp += dstScanStride;
+ }
+ // return to the first pixel of the next column
+ }
+ }
+
+ // When radiusY < h <= 2*radiusY
+ else {
+
+ // The height of the circular buffer is h
+ final int [] bufferA = new int [h];
+ final int [] bufferR = new int [h];
+ final int [] bufferG = new int [h];
+ final int [] bufferB = new int [h];
+
+ for (int j=0; j<w; j++){
+ // initialization of pointers, indice
+ // at the head of each column
+ dp = dstOff + j;
+ cp = dstOff + j;
+
+ bufferHead = 0;
+ maxIndexA = 0;
+ maxIndexR = 0;
+ maxIndexG = 0;
+ maxIndexB = 0;
+
+ pel = destPixels[cp];
+ cp += dstScanStride;
+ a = pel>>>24;
+ r = pel&0xff0000;
+ g = pel&0xff00;
+ b = pel&0xff;
+ bufferA[0] = a;
+ bufferR[0] = r;
+ bufferG[0] = g;
+ bufferB[0] = b;
+
+ for (int k=1; k<=radiusY; k++){
+ currentPixel = destPixels[cp];
+ cp += dstScanStride;
+ a1 = currentPixel>>>24;
+ r1 = currentPixel&0xff0000;
+ g1 = currentPixel&0xff00;
+ b1 = currentPixel&0xff;
+ bufferA[k] = a1;
+ bufferR[k] = r1;
+ bufferG[k] = g1;
+ bufferB[k] = b1;
+
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = k;
+ }
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = k;
+ }
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = k;
+ }
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = k;
+ }
+ }
+ // fill the first pixel of each column
+ destPixels[dp] = (a << 24) | r | g | b;
+ dp += dstScanStride;
+
+ //
+ // 1 <= i <= h-1-radiusY : The upper margin of each column.
+ //
+ for (int i=1; i<=h-radiusY-1; i++){
+ lastPixel = destPixels[cp];
+ cp += dstScanStride;
+
+ // here is the Alpha channel
+
+ a = bufferA[maxIndexA];
+ a1 = lastPixel>>>24;
+ bufferA[i+radiusY] = a1;
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = i+radiusY;
+ }
+
+ // now we deal with the Red channel
+
+ r = bufferR[maxIndexR];
+ r1 = lastPixel&0xff0000;
+ bufferR[i+radiusY] = r1;
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = i+radiusY;
+ }
+
+ // now we deal with the Green channel
+
+ g = bufferG[maxIndexG];
+ g1 = lastPixel&0xff00;
+ bufferG[i+radiusY] = g1;
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = i+radiusY;
+ }
+
+ // now we deal with the Blue channel
+
+ b = bufferB[maxIndexB];
+ b1 = lastPixel&0xff;
+ bufferB[i+radiusY] = b1;
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = i+radiusY;
+ }
+ // now we have gone through the four channels and
+ // updated the index array. then we'll pack the
+ // new max/min value according to each channel's
+ // max/min vlue
+
+ destPixels[dp] = (a << 24) | r | g | b;
+ dp += dstScanStride;
+ }
+ // Now is the inner body of the column
+ // when radiusY < h <= 2*radiusY
+ for (int i = h-radiusY; i<= radiusY; i++){
+ destPixels[dp] = destPixels[dp-dstScanStride];
+ dp += dstScanStride;
+ }
+ // The circular buffer is full now
+
+ for (int i = radiusY+1; i<h; i++){
+
+ if (maxIndexA == bufferHead){
+ a = bufferA[bufferHead+1];
+ maxIndexA = bufferHead+1;
+ for (int m= bufferHead+2; m< h; m++){
+ a1 = bufferA[m];
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = m;
+ }
+ }
+ }
+ else {
+ a = bufferA[maxIndexA];
+ }
+ if (maxIndexR == bufferHead){
+ r = bufferR[bufferHead+1];
+ maxIndexR = bufferHead+1;
+ for (int m= bufferHead+2; m< h; m++){
+ r1 = bufferR[m];
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = m;
+ }
+ }
+ }
+ else {
+ r = bufferR[maxIndexR];
+ }
+
+ if (maxIndexG == bufferHead){
+ g = bufferG[bufferHead+1];
+ maxIndexG = bufferHead+1;
+ for (int m= bufferHead+2; m< h; m++){
+ g1 = bufferG[m];
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ g = bufferG[maxIndexG];
+ }
+
+ if (maxIndexB == bufferHead){
+ b = bufferB[bufferHead+1];
+ maxIndexB = bufferHead+1;
+ for (int m= bufferHead+2; m< h; m++){
+ b1 = bufferB[m];
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ b = bufferB[maxIndexB];
+ }
+
+ // discard the leftmost element
+ bufferHead++;
+
+ destPixels[dp] = (a << 24) | r | g | b;
+ dp += dstScanStride;
+ }
+ // return to the first pixel of the next column
+ }
+ } // when radiusY < h <= 2*radiusY
+ }
+
+ /**
+ * Filters src and writes result into dest. If dest if null, then
+ * a Raster is created. If dest and src refer to the same object,
+ * then the source is modified.
+ * <p>
+ * The filtering kernel(the operation range for each pixel) is a
+ * rectangle of width 2*radiusX+1 and height radiusY+1
+ * <p>
+ * @param src the Raster to be filtered
+ * @param dest stores the filtered image. If null, a destination will
+ * be created. src and dest can refer to the same Raster, in
+ * which situation the src will be modified.
+ */
+ public WritableRaster filter(Raster src, WritableRaster dest){
+
+ //
+ //This method sorts the pixel values in the kernel window in two steps:
+ // 1. sort by row and store the result into an intermediate matrix
+ // 2. sort the intermediate matrix by column and output the max/min value
+ // into the destination matrix element
+
+ //check destation
+ if(dest!=null) checkCompatible(dest.getSampleModel());
+ else {
+ if(src==null)
+ throw new IllegalArgumentException("src should not be null when dest is null");
+ else dest = createCompatibleDestRaster(src);
+ }
+
+ final int w = src.getWidth();
+ final int h = src.getHeight();
+
+ // Access the integer buffer for each image.
+ DataBufferInt srcDB = (DataBufferInt)src.getDataBuffer();
+ DataBufferInt dstDB = (DataBufferInt)dest.getDataBuffer();
+
+ // Offset defines where in the stack the real data begin
+ final int srcOff = srcDB.getOffset();
+ final int dstOff = dstDB.getOffset();
+
+ // Stride is the distance between two consecutive column elements,
+ // in the one-dimention dataBuffer
+ final int srcScanStride = ((SinglePixelPackedSampleModel)src.getSampleModel()).getScanlineStride();
+ final int dstScanStride = ((SinglePixelPackedSampleModel)dest.getSampleModel()).getScanlineStride();
+
+ // Access the pixel value array
+ final int[] srcPixels = srcDB.getBankData()[0];
+ final int[] destPixels = dstDB.getBankData()[0];
+
+ // The pointer of src and dest indicating where the pixel values are
+ int sp, dp, cp;
+
+ // Declaration for the circular buffer's implementation
+ // These are the circular buffers' head pointer and
+ // the index pointers
+
+ // bufferHead points to the leftmost element in the circular buffer
+ int bufferHead;
+
+ int maxIndexA;
+ int maxIndexR;
+ int maxIndexG;
+ int maxIndexB;
+
+ // Temp variables
+ int pel, currentPixel, lastPixel;
+ int a,r,g,b;
+ int a1,r1,g1,b1;
+
+ // In both round, we are using an optimization approach
+ // to reduce excessive computation to sort values around
+ // the current pixel. The idea is as follows:
+ // ----------------
+ // |*|V|V|$|N|V|V|&|
+ // ----------------
+ // For example, suppose we've finished pixel"$" and come
+ // to "N", the radius is 3. Then we must have got the max/min
+ // value and index array for "$". If the max/min is at
+ // "*"(using the index array to judge this),
+ // we need to recompute a max/min and the index array
+ // for "N"; if the max/min is not at "*", we can
+ // reuse the current max/min: we simply compare it with
+ // "&", and update the max/min and the index array.
+
+ //
+ // The first round: sort by row
+ //
+ if (w<=2*radiusX){
+ specialProcessRow(src, dest);
+ }
+
+ // when the size is large enough, we can
+ // use standard optimization method
+ else {
+
+ final int [] bufferA = new int [rangeX];
+ final int [] bufferR = new int [rangeX];
+ final int [] bufferG = new int [rangeX];
+ final int [] bufferB = new int [rangeX];
+
+ for (int i=0; i<h; i++){
+ // initialization of pointers, indice
+ // at the head of each row
+ sp = srcOff + i*srcScanStride;
+ dp = dstOff + i*dstScanStride;
+ bufferHead = 0;
+ maxIndexA = 0;
+ maxIndexR = 0;
+ maxIndexG = 0;
+ maxIndexB = 0;
+
+ //
+ // j=0 : Initialization, compute the max/min and
+ // index array for the use of other pixels.
+ //
+ pel = srcPixels[sp++];
+ a = pel>>>24;
+ r = pel&0xff0000;
+ g = pel&0xff00;
+ b = pel&0xff;
+ bufferA[0] = a;
+ bufferR[0] = r;
+ bufferG[0] = g;
+ bufferB[0] = b;
+
+ for (int k=1; k<=radiusX; k++){
+ currentPixel = srcPixels[sp++];
+ a1 = currentPixel>>>24;
+ r1 = currentPixel&0xff0000;
+ g1 = currentPixel&0xff00;
+ b1 = currentPixel&0xff;
+ bufferA[k] = a1;
+ bufferR[k] = r1;
+ bufferG[k] = g1;
+ bufferB[k] = b1;
+
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = k;
+ }
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = k;
+ }
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = k;
+ }
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = k;
+ }
+ }
+ destPixels[dp++] = (a << 24) | r | g | b;
+
+ //
+ // 1 <= j <= radiusX : The left margin of each row.
+ //
+ for (int j=1; j<=radiusX; j++){
+ lastPixel = srcPixels[sp++];
+
+ // here is the Alpha channel
+
+ // we retrieve the previous max/min value
+ a = bufferA[maxIndexA];
+ a1 = lastPixel>>>24;
+ bufferA[j+radiusX] = a1;
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = j+radiusX;
+ }
+
+ // now we deal with the Red channel
+
+ r = bufferR[maxIndexR];
+ r1 = lastPixel&0xff0000;
+ bufferR[j+radiusX] = r1;
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = j+radiusX;
+ }
+
+ // now we deal with the Green channel
+
+ g = bufferG[maxIndexG];
+ g1 = lastPixel&0xff00;
+ bufferG[j+radiusX] = g1;
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = j+radiusX;
+ }
+
+ // now we deal with the Blue channel
+
+ b = bufferB[maxIndexB];
+ b1 = lastPixel&0xff;
+ bufferB[j+radiusX] = b1;
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = j+radiusX;
+ }
+ // now we have gone through the four channels and
+ // updated the index array. then we'll pack the
+ // new max/min value according to each channel's
+ // max/min vlue
+
+ destPixels[dp++] = (a << 24) | r | g | b;
+ }
+
+ //
+ // radiusX <= j <= w-1-radiusX : Inner body of the row, between
+ // left and right margins
+ //
+ for (int j=radiusX+1; j<=w-1-radiusX; j++){
+ lastPixel = srcPixels[sp++];
+ a1 = lastPixel>>>24;
+ r1 = lastPixel&0xff0000;
+ g1 = lastPixel&0xff00;
+ b1 = lastPixel&0xff;
+ bufferA[bufferHead] = a1;
+ bufferR[bufferHead] = r1;
+ bufferG[bufferHead] = g1;
+ bufferB[bufferHead] = b1;
+
+ // Alpha channel:
+ // we need to recompute a local max/min
+ // and update the max/min index
+ if (maxIndexA == bufferHead){
+ a = bufferA[0];
+ maxIndexA = 0;
+ for (int m= 1; m< rangeX; m++){
+ a1 = bufferA[m];
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ a = bufferA[maxIndexA];
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = bufferHead;
+ }
+ }
+
+ // Red channel
+ // we need to recompute a local max/min
+ // and update the index array
+
+ if (maxIndexR == bufferHead){
+ r = bufferR[0];
+ maxIndexR = 0;
+ for (int m= 1; m< rangeX; m++){
+ r1 = bufferR[m];
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ r = bufferR[maxIndexR];
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = bufferHead;
+ }
+ }
+
+ // Green channel
+ // we need to recompute a local max/min
+ // and update the index array
+
+ if (maxIndexG == bufferHead){
+ g = bufferG[0];
+ maxIndexG = 0;
+ for (int m= 1; m< rangeX; m++){
+ g1 = bufferG[m];
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ g = bufferG[maxIndexG];
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = bufferHead;
+ }
+ }
+
+ // Blue channel
+ // we need to recompute a local max/min
+ // and update the index array
+
+ if (maxIndexB == bufferHead){
+ b = bufferB[0];
+ maxIndexB = 0;
+ for (int m= 1; m< rangeX; m++){
+ b1 = bufferB[m];
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ b = bufferB[maxIndexB];
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = bufferHead;
+ }
+ }
+ destPixels[dp++] = (a << 24) | r | g | b;
+ bufferHead = (bufferHead+1)%rangeX;
+ }
+
+ //
+ // w-radiusX <= j < w : The right margin of the row
+ //
+
+ // Head will be updated to indicate the current head
+ // of the remaining buffer
+ int head;
+ // Tail is where the last element is
+ final int tail = (bufferHead == 0)?rangeX-1:bufferHead -1;
+ int count = rangeX-1;
+
+ for (int j=w-radiusX; j<w; j++){
+ head = (bufferHead+1)%rangeX;
+ // Dealing with Alpha Channel:
+ if (maxIndexA == bufferHead){
+ a = bufferA[tail];
+ int hd = head;
+ for(int m=1; m<count; m++) {
+ a1 = bufferA[hd];
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = hd;
+ }
+ hd = (hd+1)%rangeX;
+ }
+ }
+ // Dealing with Red Channel:
+ if (maxIndexR == bufferHead){
+ r = bufferR[tail];
+ int hd = head;
+ for(int m=1; m<count; m++) {
+ r1 = bufferR[hd];
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = hd;
+ }
+ hd = (hd+1)%rangeX;
+ }
+ }
+ // Dealing with Green Channel:
+ if (maxIndexG == bufferHead){
+ g = bufferG[tail];
+ int hd = head;
+ for(int m=1; m<count; m++) {
+ g1 = bufferG[hd];
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = hd;
+ }
+ hd = (hd+1)%rangeX;
+ }
+ }
+ // Dealing with Blue Channel:
+ if (maxIndexB == bufferHead){
+ b = bufferB[tail];
+ int hd = head;
+ for(int m=1; m<count; m++) {
+ b1 = bufferB[hd];
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = hd;
+ }
+ hd = (hd+1)%rangeX;
+ }
+ }
+ destPixels[dp++] = (a << 24) | r | g | b;
+ bufferHead = (bufferHead+1)%rangeX;
+ // we throw another element
+ count--;
+ }// end of the right margin of this row
+
+ // return to the beginning of the next row
+ }
+ }// end of the first round!
+
+ //
+ // Second round: sort by column
+ // the difference from the first round is that
+ // now we are accessing the intermediate matrix
+ //
+
+ // When the image size is smaller than the
+ // Kernel size
+ if (h<=2*radiusY){
+ specialProcessColumn(src, dest);
+ }
+
+ // when the size is large enough, we can
+ // use standard optimization method
+ else {
+ final int [] bufferA = new int [rangeY];
+ final int [] bufferR = new int [rangeY];
+ final int [] bufferG = new int [rangeY];
+ final int [] bufferB = new int [rangeY];
+
+ for (int j=0; j<w; j++){
+ // initialization of pointers, indice
+ // at the head of each column
+ dp = dstOff + j;
+ cp = dstOff + j;
+ bufferHead = 0;
+ maxIndexA = 0;
+ maxIndexR = 0;
+ maxIndexG = 0;
+ maxIndexB = 0;
+
+ // i=0 : The first pixel
+ pel = destPixels[cp];
+ cp += dstScanStride;
+ a = pel>>>24;
+ r = pel&0xff0000;
+ g = pel&0xff00;
+ b = pel&0xff;
+ bufferA[0] = a;
+ bufferR[0] = r;
+ bufferG[0] = g;
+ bufferB[0] = b;
+
+ for (int k=1; k<=radiusY; k++){
+ currentPixel = destPixels[cp];
+ cp += dstScanStride;
+ a1 = currentPixel>>>24;
+ r1 = currentPixel&0xff0000;
+ g1 = currentPixel&0xff00;
+ b1 = currentPixel&0xff;
+ bufferA[k] = a1;
+ bufferR[k] = r1;
+ bufferG[k] = g1;
+ bufferB[k] = b1;
+
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = k;
+ }
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = k;
+ }
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = k;
+ }
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = k;
+ }
+ }
+ destPixels[dp] = (a << 24) | r | g | b;
+ // go to the next element in the column.
+ dp += dstScanStride;
+
+ // 1 <= i <= radiusY : The upper margin of each row
+ for (int i=1; i<=radiusY; i++){
+ int maxI = i+radiusY;
+ // we can reuse the previous max/min value
+ lastPixel = destPixels[cp];
+ cp += dstScanStride;
+
+ // here is the Alpha channel
+ a = bufferA[maxIndexA];
+ a1 = lastPixel>>>24;
+ bufferA[maxI] = a1;
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = maxI;
+ }
+
+ // now we deal with the Red channel
+ r = bufferR[maxIndexR];
+ r1 = lastPixel&0xff0000;
+ bufferR[maxI] = r1;
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = maxI;
+ }
+
+ // now we deal with the Green channel
+ g = bufferG[maxIndexG];
+ g1 = lastPixel&0xff00;
+ bufferG[maxI] = g1;
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = maxI;
+ }
+
+ // now we deal with the Blue channel
+ b = bufferB[maxIndexB];
+ b1 = lastPixel&0xff;
+ bufferB[maxI] = b1;
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = maxI;
+ }
+ destPixels[dp] = (a << 24) | r | g | b;
+ dp += dstScanStride;
+ }
+
+ //
+ // radiusY +1 <= i <= h-1-radiusY:
+ // inner body of the column between upper and lower margins
+ //
+
+ for (int i=radiusY+1; i<=h-1-radiusY; i++){
+
+ lastPixel = destPixels[cp];
+ cp += dstScanStride;
+ a1 = lastPixel>>>24;
+ r1 = lastPixel&0xff0000;
+ g1 = lastPixel&0xff00;
+ b1 = lastPixel&0xff;
+ bufferA[bufferHead] = a1;
+ bufferR[bufferHead] = r1;
+ bufferG[bufferHead] = g1;
+ bufferB[bufferHead] = b1;
+
+ // here we check if the previous max/min value can be
+ // reused safely and, if possible, reuse the previous
+ // maximum value
+
+ // Alpha channel:
+
+ // Recompute the local max/min
+ if (maxIndexA == bufferHead){
+ a = bufferA[0];
+ maxIndexA = 0;
+ for (int m= 1; m<= 2*radiusY; m++){
+ a1 = bufferA[m];
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ a = bufferA[maxIndexA];
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = bufferHead;
+ }
+ }
+
+ // Red channel:
+
+ if (maxIndexR == bufferHead){
+ r = bufferR[0];
+ maxIndexR = 0;
+ for (int m= 1; m<= 2*radiusY; m++){
+ r1 = bufferR[m];
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ r = bufferR[maxIndexR];
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = bufferHead;
+ }
+ }
+
+ // Green channel
+ if (maxIndexG == bufferHead){
+ g = bufferG[0];
+ maxIndexG = 0;
+ for (int m= 1; m<= 2*radiusY; m++){
+ g1 = bufferG[m];
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ g = bufferG[maxIndexG];
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = bufferHead;
+ }
+ }
+
+ // Blue channel:
+ if (maxIndexB == bufferHead){
+ b = bufferB[0];
+ maxIndexB = 0;
+ for (int m= 1; m<= 2*radiusY; m++){
+ b1 = bufferB[m];
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = m;
+ }
+ }
+ }
+ // we can reuse the previous max/min value
+ else {
+ b = bufferB[maxIndexB];
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = bufferHead;
+ }
+ }
+ destPixels[dp] = (a << 24) | r | g | b;
+ dp += dstScanStride;
+ bufferHead = (bufferHead+1)%rangeY;
+ }
+
+ //
+ // h-radiusY <= i <= h-1 : The lower margin of the column
+ //
+
+ // head will be updated to indicate the current head
+ // of the remaining buffer:
+ int head;
+ // tail is where the last element in the buffer is
+ final int tail = (bufferHead == 0)?2*radiusY:bufferHead -1;
+ int count = rangeY-1;
+
+ for (int i= h-radiusY; i<h-1; i++){
+ head = (bufferHead +1)%rangeY;
+
+ if (maxIndexA == bufferHead){
+ a = bufferA[tail];
+ int hd = head;
+ for (int m=1; m<count; m++){
+ a1 = bufferA[hd];
+ if (isBetter(a1, a, doDilation)){
+ a = a1;
+ maxIndexA = hd;
+ }
+ hd = (hd+1)%rangeY;
+ }
+ }
+ if (maxIndexR == bufferHead){
+ r = bufferR[tail];
+ int hd = head;
+ for (int m=1; m<count; m++){
+ r1 = bufferR[hd];
+ if (isBetter(r1, r, doDilation)){
+ r = r1;
+ maxIndexR = hd;
+ }
+ hd = (hd+1)%rangeY;
+ }
+ }
+ if (maxIndexG == bufferHead){
+ g = bufferG[tail];
+ int hd = head;
+ for (int m=1; m<count; m++){
+ g1 = bufferG[hd];
+ if (isBetter(g1, g, doDilation)){
+ g = g1;
+ maxIndexG = hd;
+ }
+ hd = (hd+1)%rangeY;
+ }
+ }
+ if (maxIndexB == bufferHead){
+ b = bufferB[tail];
+ int hd = head;
+ for (int m=1; m<count; m++){
+ b1 = bufferB[hd];
+ if (isBetter(b1, b, doDilation)){
+ b = b1;
+ maxIndexB = hd;
+ }
+ hd = (hd+1)%rangeY;
+ }
+ }
+ destPixels[dp] = (a << 24) | r | g | b;
+ dp += dstScanStride;
+ bufferHead = (bufferHead+1)%rangeY;
+ // we throw out this useless element
+ count--;
+ }
+ // return to the beginning of the next column
+ }
+ }// end of the second round!
+
+ return dest;
+ }// end of the filter() method for Raster
+
+ /**
+ * This implementation of filter does the morphology operation
+ * on a premultiplied alpha image. This tends to muddy the
+ * colors. so something that is supposed to be a mostly
+ * transparent bright red may well become a muddy opaque red.
+ * Where as I think it should become a bright opaque red. Which
+ * is the result you would get if you were using unpremult data.
+ */
+ public BufferedImage filter(BufferedImage src, BufferedImage dest){
+ if (src == null)
+ throw new NullPointerException("Source image should not be null");
+
+ BufferedImage origSrc = src;
+ BufferedImage finalDest = dest;
+
+ if (!isCompatible(src.getColorModel(), src.getSampleModel())) {
+ src = new BufferedImage(src.getWidth(), src.getHeight(),
+ BufferedImage.TYPE_INT_ARGB_PRE);
+ GraphicsUtil.copyData(origSrc, src);
+ }
+ else if (!src.isAlphaPremultiplied()) {
+ // Get a Premultipled CM.
+ ColorModel srcCM, srcCMPre;
+ srcCM = src.getColorModel();
+ srcCMPre = GraphicsUtil.coerceColorModel(srcCM, true);
+
+ src = new BufferedImage(srcCMPre, src.getRaster(),
+ true, null);
+
+ GraphicsUtil.copyData(origSrc, src);
+ }
+
+
+ if (dest == null) {
+ dest = createCompatibleDestImage(src, null);
+ finalDest = dest;
+ } else if (!isCompatible(dest.getColorModel(),
+ dest.getSampleModel())) {
+ dest = createCompatibleDestImage(src, null);
+ } else if (!dest.isAlphaPremultiplied()) {
+ // Get a Premultipled CM.
+ ColorModel dstCM, dstCMPre;
+ dstCM = dest.getColorModel();
+ dstCMPre = GraphicsUtil.coerceColorModel(dstCM, true);
+
+ dest = new BufferedImage(dstCMPre, finalDest.getRaster(),
+ true, null);
+ }
+
+ filter(src.getRaster(), dest.getRaster());
+
+ // Check to see if we need to 'fix' our source (divide out alpha).
+ if ((src.getRaster() == origSrc.getRaster()) &&
+ (src.isAlphaPremultiplied() != origSrc.isAlphaPremultiplied())) {
+ // Copy our source back the way it was...
+ GraphicsUtil.copyData(src, origSrc);
+ }
+
+ // Check to see if we need to store our result...
+ if ((dest.getRaster() != finalDest.getRaster()) ||
+ (dest.isAlphaPremultiplied() != finalDest.isAlphaPremultiplied())){
+ // Coerce our source back the way it was requested...
+ GraphicsUtil.copyData(dest, finalDest);
+ }
+
+ return finalDest;
+ }
+ /*
+ * This commented out implementation of filter does the
+ * morphology operation on unpremultiplied alpha image data.
+ * This tends to leave colors bright.
+ */
+ /*
+ public BufferedImage filter(BufferedImage src, BufferedImage dest){
+ if (src == null && dest == null)
+ throw new NullPointerException("Source image should not be null");
+
+ BufferedImage origSrc = src;
+ BufferedImage finalDest = dest;
+
+ if (!isCompatible(src.getColorModel(), src.getSampleModel())) {
+ src = new BufferedImage(src.getWidth(), src.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ GraphicsUtil.copyData(origSrc, src);
+ }
+ else if (src.isAlphaPremultiplied()) {
+ ColorModel srcCM, srcCMUnpre;
+ srcCM = src.getColorModel();
+ srcCMUnpre = GraphicsUtil.coerceColorModel(srcCM, false);
+ src = new BufferedImage(srcCMUnpre, src.getRaster(),
+ false, null);
+
+ GraphicsUtil.copyData(origSrc, src);
+ }
+
+
+ if (dest == null) {
+ dest = new BufferedImage(src.getWidth(), src.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ finalDest = dest;
+ } else if (!isCompatible(dest.getColorModel(),
+ dest.getSampleModel())) {
+ dest = new BufferedImage(src.getWidth(), src.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ } else if (dest.isAlphaPremultiplied()) {
+ ColorModel dstCM, dstCMUnpre;
+ dstCM = dest.getColorModel();
+ dstCMUnpre = GraphicsUtil.coerceColorModel(dstCM, false);
+ dest = new BufferedImage(dstCMUnpre, finalDest.getRaster(),
+ false, null);
+ }
+
+ // We now have two compatible images. We can safely filter the rasters
+ filter(src.getRaster(), dest.getRaster());
+
+ // Check to see if we need to 'fix' our source (divide out alpha).
+ if ((src.getRaster() == origSrc.getRaster()) &&
+ (src.isAlphaPremultiplied() != origSrc.isAlphaPremultiplied())) {
+ GraphicsUtil.copyData(src, origSrc);
+ }
+
+ // Check to see if we need to store our result...
+ if ((dest.getRaster() != finalDest.getRaster()) ||
+ (dest.isAlphaPremultiplied() != finalDest.isAlphaPremultiplied())){
+ // Coerce our source back the way it was...
+ System.out.println("Dest: " + dest.isAlphaPremultiplied() +
+ " finalDest: " +
+ finalDest.isAlphaPremultiplied());
+
+ GraphicsUtil.copyData(dest, finalDest);
+ }
+ return finalDest;
+ }
+ */
+}
+
+
+
+
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MorphologyOp.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MultiplyAlphaRed.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MultiplyAlphaRed.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MultiplyAlphaRed.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MultiplyAlphaRed.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,331 @@
+/*
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ */
+package org.apache.flex.forks.batik.ext.awt.image.rendered;
+
+import java.awt.Rectangle;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * This implements a masking operation by multiply the alpha channel of
+ * one image by a luminance image (the mask).
+ *
+ * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
+ * @version $Id: MultiplyAlphaRed.java 478276 2006-11-22 18:33:37Z dvholten $ */
+public class MultiplyAlphaRed extends AbstractRed {
+
+ /**
+ * Multiply the alpha of one image with a mask image.
+ * The size of the resultant image is the intersection of the
+ * two image bounds. If you want the end image to be the size
+ * of one or the other please use the PadRed operator.
+ *
+ * @param src The image to convert to multiply the alpha of
+ * @param alpha The mask image to multiply the alpha channel of src
+ * with.
+ */
+ public MultiplyAlphaRed(CachableRed src, CachableRed alpha) {
+ super(makeList(src, alpha),
+ makeBounds(src,alpha),
+ fixColorModel(src),
+ fixSampleModel(src),
+ src.getTileGridXOffset(),
+ src.getTileGridYOffset(),
+ null);
+ }
+
+ public boolean is_INT_PACK_BYTE_COMP(SampleModel srcSM,
+ SampleModel alpSM) {
+ // Check SampleModel types DirectColorModel
+ if(!(srcSM instanceof SinglePixelPackedSampleModel)) return false;
+ if(!(alpSM instanceof ComponentSampleModel)) return false;
+
+ // Check transfer types
+ if(srcSM.getDataType() != DataBuffer.TYPE_INT) return false;
+ if(alpSM.getDataType() != DataBuffer.TYPE_BYTE) return false;
+
+
+ SinglePixelPackedSampleModel sppsm;
+ sppsm = (SinglePixelPackedSampleModel)srcSM;
+
+ int [] masks = sppsm.getBitMasks();
+ if(masks.length != 4) return false;
+ if(masks[0] != 0x00ff0000) return false;
+ if(masks[1] != 0x0000ff00) return false;
+ if(masks[2] != 0x000000ff) return false;
+ if(masks[3] != 0xff000000) return false;
+
+ ComponentSampleModel csm;
+ csm = (ComponentSampleModel)alpSM;
+ if (csm.getNumBands() != 1) return false;
+ if (csm.getPixelStride() != 1) return false;
+
+ return true;
+ }
+
+ public WritableRaster INT_PACK_BYTE_COMP_Impl (WritableRaster wr) {
+ // Get my source.
+ CachableRed srcRed = (CachableRed)getSources().get(0);
+ CachableRed alphaRed = (CachableRed)getSources().get(1);
+
+ // Already has alpha channel so we use it.
+ srcRed.copyData(wr);
+
+ Rectangle rgn = wr.getBounds();
+ rgn = rgn.intersection(alphaRed.getBounds());
+
+ Raster r = alphaRed.getData(rgn);
+
+ ComponentSampleModel csm;
+ csm = (ComponentSampleModel)r.getSampleModel();
+ final int alpScanStride = csm.getScanlineStride();
+
+ DataBufferByte alpDB = (DataBufferByte)r.getDataBuffer();
+ final int alpBase
+ = (alpDB.getOffset() +
+ csm.getOffset(rgn.x-r.getSampleModelTranslateX(),
+ rgn.y-r.getSampleModelTranslateY()));
+
+
+ // Access the pixel data array
+ final byte[] alpPixels = alpDB.getBankData()[0];
+
+ SinglePixelPackedSampleModel sppsm;
+ sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel();
+ final int srcScanStride = sppsm.getScanlineStride();
+
+ DataBufferInt srcDB = (DataBufferInt)wr.getDataBuffer();
+ final int srcBase
+ = (srcDB.getOffset() +
+ sppsm.getOffset(rgn.x-wr.getSampleModelTranslateX(),
+ rgn.y-wr.getSampleModelTranslateY()));
+
+ // Access the pixel data array
+ final int[] srcPixels = srcDB.getBankData()[0];
+
+ ColorModel cm = srcRed.getColorModel();
+
+ if (cm.isAlphaPremultiplied()) {
+ // For alpha premult we need to multiply all comps.
+ for (int y=0; y<rgn.height; y++) {
+ int sp = srcBase + y*srcScanStride;
+ int ap = alpBase + y*alpScanStride;
+ int end = sp + rgn.width;
+
+ while (sp<end) {
+ int a = ((int)alpPixels[ap++])&0xFF;
+ final int pix = srcPixels[sp];
+ srcPixels[sp] =
+ ((((((pix>>>24) ) *a)&0xFF00)<<16) |
+ (((((pix>>>16)&0xFF) *a)&0xFF00)<<8 ) |
+ (((((pix>>> 8)&0xFF) *a)&0xFF00) ) |
+ (((((pix )&0xFF) *a)&0xFF00)>>8 ));
+ sp++;
+ }
+ }
+
+ } else {
+ // For non-alpha premult we only need to multiply alpha.
+ for (int y=0; y<rgn.height; y++) {
+ int sp = srcBase + y*srcScanStride;
+ int ap = alpBase + y*alpScanStride;
+ int end = sp + rgn.width;
+ while (sp<end) {
+ int a = ((int)alpPixels[ap++])&0xFF;
+ int sa = srcPixels[sp]>>>24;
+ srcPixels[sp] = ((((sa*a) & 0xFF00)<<16)|
+ srcPixels[sp]&0x00FFFFFF);
+ sp++;
+ }
+ }
+ }
+
+ return wr;
+ }
+
+ public WritableRaster copyData(WritableRaster wr) {
+ // Get my source.
+ CachableRed srcRed = (CachableRed)getSources().get(0);
+ CachableRed alphaRed = (CachableRed)getSources().get(1);
+
+ if (is_INT_PACK_BYTE_COMP(srcRed.getSampleModel(),
+ alphaRed.getSampleModel()))
+ return INT_PACK_BYTE_COMP_Impl(wr);
+
+ ColorModel cm = srcRed.getColorModel();
+ if (cm.hasAlpha()) {
+ // Already has alpha channel so we use it.
+ srcRed.copyData(wr);
+
+ Rectangle rgn = wr.getBounds();
+ if (rgn.intersects(alphaRed.getBounds()))
+ rgn = rgn.intersection(alphaRed.getBounds());
+ else
+ return wr;
+
+ int [] wrData = null;
+ int [] alphaData = null;
+
+ Raster r = alphaRed.getData(rgn);
+ int w = rgn.width;
+
+ final int bands = wr.getSampleModel().getNumBands();
+
+ if (cm.isAlphaPremultiplied()) {
+ for (int y=rgn.y; y<rgn.y+rgn.height; y++) {
+ wrData = wr.getPixels (rgn.x, y, w, 1, wrData);
+ alphaData = r .getSamples(rgn.x, y, w, 1, 0, alphaData);
+ int i=0, a, b;
+ // 4 is the most common case.
+ // 2 is probably next most common...
+ switch (bands) {
+ case 2:
+ for (int x=0; x<alphaData.length; x++) {
+ a = alphaData[x]&0xFF;
+ wrData[i] = ((wrData[i]&0xFF)*a)>>8; ++i;
+ wrData[i] = ((wrData[i]&0xFF)*a)>>8; ++i;
+ }
+ break;
+ case 4:
+ for (int x=0; x<alphaData.length; x++) {
+ a = alphaData[x]&0xFF;
+ wrData[i] = ((wrData[i]&0xFF)*a)>>8; ++i;
+ wrData[i] = ((wrData[i]&0xFF)*a)>>8; ++i;
+ wrData[i] = ((wrData[i]&0xFF)*a)>>8; ++i;
+ wrData[i] = ((wrData[i]&0xFF)*a)>>8; ++i;
+ }
+ break;
+ default:
+ for (int x=0; x<alphaData.length; x++) {
+ a = alphaData[x]&0xFF;
+ for (b=0; b<bands; b++) {
+ wrData[i] = ((wrData[i]&0xFF)*a)>>8;
+ ++i;
+ }
+ }
+ }
+ wr.setPixels(rgn.x, y, w, 1, wrData);
+ }
+ } else {
+ int b = srcRed.getSampleModel().getNumBands()-1;
+ for (int y=rgn.y; y<rgn.y+rgn.height; y++) {
+ wrData = wr.getSamples(rgn.x, y, w, 1, b, wrData);
+ alphaData = r .getSamples(rgn.x, y, w, 1, 0, alphaData);
+ for (int i=0; i<wrData.length; i++) {
+ wrData[i] = ((wrData[i]&0xFF)*(alphaData[i]&0xFF))>>8;
+ }
+ wr.setSamples(rgn.x, y, w, 1, b, wrData);
+ }
+ }
+
+ return wr;
+ }
+
+ // No alpha in source, so we hide the alpha channel in wr and
+ // have our source fill wr with color info...
+ int [] bands = new int[wr.getNumBands()-1];
+ for (int i=0; i<bands.length; i++)
+ bands[i] = i;
+
+ WritableRaster subWr;
+ subWr = wr.createWritableChild(wr.getMinX(), wr.getMinY(),
+ wr.getWidth(), wr.getHeight(),
+ wr.getMinX(), wr.getMinY(),
+ bands);
+
+ srcRed.copyData(subWr);
+
+ Rectangle rgn = wr.getBounds();
+ rgn = rgn.intersection(alphaRed.getBounds());
+
+
+ bands = new int [] { wr.getNumBands()-1 };
+ subWr = wr.createWritableChild(rgn.x, rgn.y,
+ rgn.width, rgn.height,
+ rgn.x, rgn.y,
+ bands);
+ alphaRed.copyData(subWr);
+
+ return wr;
+ }
+
+ public static List makeList(CachableRed src1, CachableRed src2) {
+ List ret = new ArrayList(2);
+ ret.add(src1);
+ ret.add(src2);
+ return ret;
+ }
+
+ public static Rectangle makeBounds(CachableRed src1, CachableRed src2) {
+ Rectangle r1 = src1.getBounds();
+ Rectangle r2 = src2.getBounds();
+ return r1.intersection(r2);
+ }
+
+ public static SampleModel fixSampleModel(CachableRed src) {
+ ColorModel cm = src.getColorModel();
+ SampleModel srcSM = src.getSampleModel();
+
+ if (cm.hasAlpha())
+ return srcSM;
+
+ int w = srcSM.getWidth();
+ int h = srcSM.getHeight();
+ int b = srcSM.getNumBands()+1;
+ int [] offsets = new int[b];
+ for (int i=0; i < b; i++)
+ offsets[i] = i;
+
+ // Really should check DataType range in srcSM...
+ return new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE,
+ w, h, b, w*b, offsets);
+ }
+
+ public static ColorModel fixColorModel(CachableRed src) {
+ ColorModel cm = src.getColorModel();
+
+ if (cm.hasAlpha())
+ return cm;
+
+ int b = src.getSampleModel().getNumBands()+1;
+ int [] bits = new int[b];
+ for (int i=0; i < b; i++)
+ bits[i] = 8;
+
+ ColorSpace cs = cm.getColorSpace();
+
+ return new ComponentColorModel(cs, bits, true, false,
+ Transparency.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+ }
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/MultiplyAlphaRed.java
------------------------------------------------------------------------------
svn:eol-style = native