Return-Path: X-Original-To: apmail-incubator-flex-commits-archive@minotaur.apache.org Delivered-To: apmail-incubator-flex-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 6CC50D9DA for ; Thu, 25 Oct 2012 19:03:49 +0000 (UTC) Received: (qmail 82438 invoked by uid 500); 25 Oct 2012 19:03:49 -0000 Delivered-To: apmail-incubator-flex-commits-archive@incubator.apache.org Received: (qmail 82415 invoked by uid 500); 25 Oct 2012 19:03:49 -0000 Mailing-List: contact flex-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: flex-dev@incubator.apache.org Delivered-To: mailing list flex-commits@incubator.apache.org Received: (qmail 82408 invoked by uid 99); 25 Oct 2012 19:03:49 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 25 Oct 2012 19:03:49 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 25 Oct 2012 19:03:37 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 43E782388C8B; Thu, 25 Oct 2012 19:02:00 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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... Date: Thu, 25 Oct 2012 19:01:49 -0000 To: flex-commits@incubator.apache.org From: cframpton@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20121025190200.43E782388C8B@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 Sheng Pei + * @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 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>>24; + r = pel&0xff0000; + g = pel&0xff00; + b = pel&0xff; + + for (int k=1; k>>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>>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>>24; + r = pel&0xff0000; + g = pel&0xff00; + b = pel&0xff; + + for (int k=1; k>>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>>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 + * The filtering kernel(the operation range for each pixel) is a + * rectangle of width 2*radiusX+1 and height radiusY+1 + *

+ * @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>>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>>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; iThomas DeWeese + * @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>>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>>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>8; ++i; + wrData[i] = ((wrData[i]&0xFF)*a)>>8; ++i; + } + break; + case 4: + for (int x=0; x>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>8; + ++i; + } + } + } + wr.setPixels(rgn.x, y, w, 1, wrData); + } + } else { + int b = srcRed.getSampleModel().getNumBands()-1; + for (int y=rgn.y; y>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