Hello Bill & Crew,
allow me to congratulate you on your product, which was a lot of fun to evaluate. One of the first things that I needed was a way to invert a rectangle that I had drawn with a rubberband on a monochrome scanned image. I couldn't really find anything in your manuals (the best thing was a complete invert), so I went and created my own. I submit this in the hopes that this will be helpful to someone; please understand that this comes without warranties of any kind.
This is a custom ImageRegionCommand, see the comments for usage.
public class RegionInversionCommand : ImageRegionCommand {
// Author: Lutz Mueller, 9/13/2006
// modified from original handbook example, which did a low level invert of a whole page.
//
// This will invert a region. We still only support 1bpp, and it would have to be
// modified for big endian architectures. Anyway, you can call it like this:
//
// RegionInversionCommand myCommand = new RegionInversionCommand();
// myCommand.RegionOfInterest = new RegionOfInterest(myRectangle);
// myWorkspaceViewer.ApplyCommand(myCommand);
public RegionInversionCommand() { }
// operate on the source image
public override bool InPlaceProcessing { get { return true; } }
// only natively support 1 bit images
private static PixelFormat[] _supportedFormats = new PixelFormat[1]
{PixelFormat.Pixel1bppIndexed};
public override PixelFormat[] SupportedPixelFormats { get { return _supportedFormats; } }
protected override void VerifyProperties(AtalaImage image) {
// nothing needed
}
protected override AtalaImage PerformActualCommand(AtalaImage source, AtalaImage dest,System.Drawing.Rectangle imageArea, ref ImageResults results) {
// the case imageArea.Width=0 or imageArea.Height=0 leads to an exception prior
// to entering here, so we don't have to catch it.
int widthInBytes = source.RowStride;
unsafe {
// get a pointer to image memory
byte *imageStart = (byte*)source.ImageData.ToPointer();
// The rectangle to be inverted will not generally adhere to byte boundaries.
// While we can simply negate bytes that are completely in the area to be inverted,
// we need to mask the bytes on the left and right sides of the rectangle, because
// they will only be partially inverted.
byte[] lmasks = { 255, 127, 63, 31, 15, 7, 3, 1 };
byte[] rmasks = { 128, 192, 224, 240, 248, 252, 254, 255 };
// determine the byte offsets of the partially inverted bytes.
int leadOffset = (imageArea.Left / 8);
int trailOffset = (imageArea.Right / 8);
// determine the number of bytes
int leadShift = imageArea.Left % 8;
int trailShift = imageArea.Right % 8;
byte leadMask = lmasks[leadShift];
byte trailMask = rmasks[trailShift];
// first, we cover a special case: the rectangle to be inverted
// lives in the same byte (as far as x is concerned). We iterate
// over y and XOR with the combined masks.
byte* row = imageStart + imageArea.Top * widthInBytes + leadOffset;
if (leadOffset == trailOffset) {
byte mask = (byte)(leadMask & trailMask);
for (int y = imageArea.Top; y < imageArea.Bottom; y++) {
*row = (byte)(*row ^ mask);
row = row + widthInBytes;
}
return null;
}
// we can now assume that we can use our separate masks for lead and
// trail byte. The solid run in between can still be 0 bytes long.
int runLength = (trailOffset - leadOffset) -1;
for (int y = imageArea.Top; y < imageArea.Bottom; y++) {
byte* runner = row;
*runner = (byte)(*runner ^ leadMask);
runner++;
for (int x = 0; x < runLength; x++) { // may be 0 times
*runner = (byte)(~*runner);
runner++;
}
*runner = (byte)(*runner ^ trailMask);
row = row + widthInBytes; // next image row
} // for y
}
return null;
}
}