Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

Can someone please try to figure out what is happening here? I'm trying to apply the Multiply filter, similar to the one Photoshop uses by crossing the RGB values between two images. I'm not a pointer* guy at all and most of the code was taken from internet so I can't really say what is wrong.

 

The function is currently working partially, that is, it applies the filter only over a third of the image from top to bottom on the left side.

 

private void button2_Click(object sender, EventArgs e)
       {
           Bitmap srcA = (Bitmap)this.lbl_map.Image;
           Bitmap srcB = (Bitmap)this.lbl_paper.Image;
           BitmapData dataA = SetImageToProcess(srcA, new Rectangle(0, 0, 1280, 800));
           BitmapData dataB = SetImageToProcess(srcB, new Rectangle(0, 0, 1280, 800));

           int width = dataA.Width;
           int height = dataA.Height;
           int offset = dataA.Stride - width;

           unsafe
           {
               byte* ptrA = (byte*)dataA.Scan0;
               byte* ptrB = (byte*)dataB.Scan0;

               for (int y = 0; y < height; y++)
               {
                   for (int x = 0; x < width; x++, ++ptrA, ++ptrB)
                   {
                       int result = (ptrB[0] * ptrA[0]) / 255;
                       ptrA[0] = (byte)result > (byte)255 ? (byte)255 : (byte)result;
                   }
                   ptrA += offset;
                   ptrB += offset;
               }
           }
           srcA.UnlockBits(dataA);
           srcB.UnlockBits(dataB);

           this.BackgroundImage = srcA;
       }

       static public BitmapData SetImageToProcess(Bitmap image, Rectangle roi)
       {
           if (image != null)
               return image.LockBits(
                   roi,
                   ImageLockMode.ReadWrite,
                   image.PixelFormat);

           return null;
       }

 

I really wish I could understand a bit more of how this function actually works. The filter seems to play instantaneously against the 10sec it takes doing it with GDI+.

 

Thanks

  • Leaders
Posted

It looks like there might be a couple of problems with your pointer math. I don't play with pointers every day, so I can't promise I'll get it 100% either, but here's what I'm noticing:

 

First, I'm a little confused by this:

int offset = dataA.Stride - width;

It looks like the value of "offset" is meant to make up the difference between the length of a row's data and the row's stride (i.e. offset specifies the row's padding). That seems like a confusing way to deal with stride.

 

What I would do is have two pointer variables per image. In addition to ptrA and ptrB, I would have what I'll call rowStartA and rowStartB. You would initalize rowStartA to dataA.Scan0, and rowStartB likewise. Then, after you process each row, add dataA.Stride to rowStartA to get the starting address of the next row. Before you loop over the pixels in each row, you would initialize prtA to the current value of rowStartA, and then increment ptrA after each byte is processed. So, something like this...

rowStartA = dataA.Scan0
rowStartB = dataB.Scan0

loop over Y
   ptrA = rowStartA
   prtB = rowStartB

   loop over X
       process byte
       ptrA ++
       ptrB ++
   end loop

   rowStartA += dataA.Stride
   rowStartB += dataB.Stride
end loop

 

Secondly, your pointers walk through bytes. If I understand correctly, you are processing one byte per pixel. With a 24-bit format, it would be three bytes per pixel. With 32-bit, it would be four bytes per pixel, but you would want to skip over the alpha byte. This is what I imagine code would typically look like for an ARGB image, not taking alpha blending into account (hopefully I've got the byte order correct):

for (int x = 0; x < width; x++)
{
   // Blue
   int result = (ptrB[0] * ptrA[0]) / 255;
   ptrA[0] = (byte)result > (byte)255 ? (byte)255 : (byte)result;
   prtA++; ptrB++;

   // Green
   int result = (ptrB[0] * ptrA[0]) / 255;
   ptrA[0] = (byte)result > (byte)255 ? (byte)255 : (byte)result;
   prtA++; ptrB++;

   // Red
   int result = (ptrB[0] * ptrA[0]) / 255;
   ptrA[0] = (byte)result > (byte)255 ? (byte)255 : (byte)result;
   prtA++; ptrB++;

   // Alpha (ignored) (or you can just copy the alpha channel from image A to B)
   prtA++; ptrB++;
}

For a 24-bit format, just get rid of the last pair of increments.

 

Hopefully that helps.

[sIGPIC]e[/sIGPIC]
Posted

Towards understanding (and working with) Scan0 and stride

 

It looks like the value of "offset" is meant to make up the difference between the length of a row's data and the row's stride (i.e. offset specifies the row's padding). That seems like a confusing way to deal with stride.
I had a lot of trouble understanding Scan0 and Stride also,

but this Bob Powell article about accessing image data helped me.

 

It has a good diagram.

Posted (edited)

Thanks to you both, I finally managed to understand how the whole thing works! This will be so cool in future! I think this way I can actually replace most of my GDI+ rendering procedures with pointers*.

 

I'm still amazed by its speed! It is instantaneously! Too bad I still need to use the DrawImageUnscaled() to draw the backbuffer to avoid flickering. Nonetheless it's still be awesome! :D

Edited by EFileTahi-A
  • 2 years later...
Posted

Wow, at has been a long time!

 

For some reason, I cannot make this function work properly when image B is a PNG -- I was experimenting my PNG's transparency (a black gradient with 100% black opacity on the left reaching 0% on the right most) over a BMP. Any ideas?

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...