int[] errBuf = new int[sourceWidthPadded*NBANDS];
// Retrieve format tags.
RasterFormatTag[] formatTags = getFormatTags();
RasterAccessor srcAccessor =
new RasterAccessor(source,
new Rectangle(startX, startY,
source.getWidth(),
source.getHeight()),
formatTags[0], getSourceImage(0).getColorModel());
RasterAccessor dstAccessor =
new RasterAccessor(dest, destRect, formatTags[1], getColorModel());
// Set pixel and line strides.
int srcPixelStride = srcAccessor.getPixelStride();
int srcScanlineStride = srcAccessor.getScanlineStride();
int dstPixelStride = dstAccessor.getPixelStride();
int dstScanlineStride = dstAccessor.getScanlineStride();
// Set data arrays.
byte[] srcData0 = srcAccessor.getByteDataArray(0);
byte[] srcData1 = srcAccessor.getByteDataArray(1);
byte[] srcData2 = srcAccessor.getByteDataArray(2);
byte[] dstData = dstAccessor.getByteDataArray(0);
// Initialize line offset in each band.
int srcLine0 = srcAccessor.getBandOffset(0);
int srcLine1 = srcAccessor.getBandOffset(1);
int srcLine2 = srcAccessor.getBandOffset(2);
int dstLine = dstAccessor.getBandOffset(0);
//
// For each line, calculate and distribute the error into
// a 3 line error buffer (one line for each band).
// Also accumulate the contributions of the 3 bands
// into the same line of the temporary output buffer.
//
// The error buffer starts out with all zeroes as the
// amount of error to propagate forward.
//
for (int y = startY; y <= endY; y++) {
// Initialize pixel offset in each line in each band.
int srcPixel0 = srcLine0;
int srcPixel1 = srcLine1;
int srcPixel2 = srcLine2;
int dstPixel = dstLine;
//
// Determine the error and index contribution for
// the each band. Keep the transitory errors
// (errA, errC and errD) in local variables
// (hopefully registers). The calculated value
// of errB gets put into the error buffer, to be used
// on the next line.
//
// This is the logic here. Floyd-Steinberg dithering
// distributes errors to four neighboring pixels,
// as shown below. X is the pixel being operated on.
//
// 7/16 of the error goes to pixel A
// 3/16 of the error goes to pixel B
// 5/16 of the error goes to pixel C
// 1/16 of the error goes to pixel D
//
// X A
// B C D
//
// The error distributed to pixel A is reused immediately
// in the calculation of the next pixel on the same line.
// The errors distributed to B, C and D will be used on the
// following line. As we move from left to right, the
// new error distributed to B gets added to the error
// at the previous C. Likewise, the new C error gets added
// to the previous D error. So only the errors propagating
// to position B survive in the saved error buffer. The
// only exception is at the line end, where error C must be
// saved. The scheme is shown below.
//
// XA
// BCD
// BCD
// BCD
// BCD
//
// Treat the error buffer as pixel sequential.
// This lets us use a single pointer with offsets
// for the entries for all three bands.
//
//
// Zero the error holders for all bands
// The bands are called Red, Grn and Blu, but are
// really just the first, second and third bands.
//
int errRedA = 0;
int errRedC = 0;
int errRedD = 0;
int errGrnA = 0;
int errGrnC = 0;
int errGrnD = 0;
int errBluA = 0;
int errBluC = 0;
int errBluD = 0;
int pErr = 0;
int dstOffset = 0;
for (int x = startX; x <= endX; x++) {
//
// First band (Red)
// The color index is initialized here.
// Set the table pointer to the "Red" band
//
int pTab = UNDERSHOOT;
int adjVal =
((errRedA + errBuf[pErr+3] + 8) >> 4) +
(int)(srcData0[srcPixel0] & 0xff);
srcPixel0 += srcPixelStride;
int tabval = ditherTable[pTab+adjVal];
int err = tabval >> 8;
int err1 = err;
int index = (tabval & 0xff);
int err2 = err + err;
errBuf[pErr] = errRedC + (err += err2); // 3/16 (B)
errRedC = errRedD + (err += err2); // 5/16 (C)
errRedD = err1; // 1/16 (D)
errRedA = (err += err2); // 7/16 (A)
//
// Second band (Green)
// Set the table pointer to the "Green" band
// The color index is incremented here.
//
pTab += TOTALGRAYS;
adjVal =
((errGrnA + errBuf[pErr+4] + 8) >> 4) +
(int)(srcData1[srcPixel1] & 0xff);
srcPixel1 += srcPixelStride;
tabval = ditherTable[pTab+adjVal];
err = tabval >> 8;
err1 = err;
index += (tabval & 0xff);
err2 = err + err;
errBuf[pErr+1] = errGrnC + (err += err2);
errGrnC = errGrnD + (err += err2);
errGrnD = err1;
errGrnA = (err += err2);
pTab += TOTALGRAYS;
//
// Third band (Blue)
// Set the table pointer to the "Blue" band
// The color index is incremented here.
//
adjVal =
((errBluA + errBuf[pErr+5] + 8) >> 4) +
(int)(srcData2[srcPixel2] & 0xff);
srcPixel2 += srcPixelStride;
tabval = ditherTable[pTab+adjVal];
err = tabval >> 8;
err1 = err;
index += (tabval & 0xff);
err2 = err + err;
errBuf[pErr+2] = errBluC + (err += err2);
errBluC = errBluD + (err += err2);
errBluD = err1;
errBluA = (err += err2);
// Save the result in the output data buffer.
dstData[dstPixel] = (byte)(index&0xff);
dstPixel += dstPixelStride;
pErr += 3;
} // End pixel loop
//
// Save last error in line
//
int last = 3 * (sourceWidthPadded - 2);
errBuf[last] = errRedC;
errBuf[last+1] = errGrnC;
errBuf[last+2] = errBluC;
// Increment offset in each band to next line.
srcLine0 += srcScanlineStride;
srcLine1 += srcScanlineStride;
srcLine2 += srcScanlineStride;
dstLine += dstScanlineStride;
} // End scanline loop
// Make sure that the output data is copied to the destination.
dstAccessor.copyDataToRaster();
}