EFileTahi-A Posted November 14, 2012 Posted November 14, 2012 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 Quote
Leaders snarfblam Posted November 14, 2012 Leaders Posted November 14, 2012 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. Quote [sIGPIC]e[/sIGPIC]
bokeh Posted November 14, 2012 Posted November 14, 2012 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. Quote
EFileTahi-A Posted November 15, 2012 Author Posted November 15, 2012 (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 November 15, 2012 by EFileTahi-A Quote
EFileTahi-A Posted February 24, 2015 Author Posted February 24, 2015 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? Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.