in batik-awt-util/src/main/java/org/apache/batik/ext/awt/image/rendered/MorphologyOp.java [833:1525]
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