Atalasoft
Welcome to Atalasoft Community Sign in | Join | Help
in

fast 1bpp region invert

Last post 13 Sep 2006, 5:48 PM by lmueller. 0 replies.
Sort Posts: Previous Next
  •  13 Sep 2006, 5:48 PM 10851

    fast 1bpp region invert

    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;

       }

    }

View as RSS news feed in XML