Results 1 to 9 of 9

Thread: I thought this might belong here..

  1. #1

    Thread Starter
    G&G Moderator chemicalNova's Avatar
    Join Date
    Jun 2002
    Location
    Victoria, Australia
    Posts
    4,246

    I thought this might belong here..

    Howdy all,

    My question is to do with programming, specifically LockBits on the .NET framework.

    I'm working with a single dimensional array, representing a picture. The array is structured, so the first 3 elements have the r g and b values of the first pixel, the 4th, 5th and 6th elements have the r g and b values of the second pixel, and so on. The pixels are stored by rows (not columns).

    I'm trying to write a "SetPixel" function using this. What I have so far is this:
    PHP Code:
    private void SetPixel(int xint ybyte rbyte gbyte bref byte[] rgbValuesint Width)
    {
        
    int px = (((Width) * 3) + (3));
        
    rgbValues[px] = b;
        
    rgbValues[px++] = g;
        
    rgbValues[px++] = r;

    This however, doesn't seem to work. It works in the sense that no error occur, but if I use this:
    PHP Code:
    SetPixel(320y000ref rgbValuesWidth);
    SetPixel(321y000ref rgbValuesWidth);
    SetPixel(322y000ref rgbValuesWidth);
    SetPixel(323y000ref rgbValuesWidth);
    SetPixel(324y000ref rgbValuesWidth);
    SetPixel(325y000ref rgbValuesWidth);
    SetPixel(326y000ref rgbValuesWidth);
    SetPixel(327y000ref rgbValuesWidth);
    SetPixel(328y000ref rgbValuesWidth);
    SetPixel(329y000ref rgbValuesWidth); 
    ... there are gaps between the plotted pixels, both horizontally and vertically.



    My question to you is:

    How would I correctly calculate where to place the pixels in the setpixel function? Nothing I seem to try works and its starting to make my head explode (slowly ). If anyone has an answer I would be very appreciative.

    Thanks in advance,

    chem

    Visual Studio 6, Visual Studio.NET 2005, MASM

  2. #2
    type Woss is new Grumpy; wossname's Avatar
    Join Date
    Aug 2002
    Location
    #!/bin/bash
    Posts
    5,682

    Re: I thought this might belong here..

    Try this...

    Code:
    private void SetPixel(int x, int y, byte r, byte g, byte b, ref byte[] rgbValues, int Width)
    {
        int px = ((y*width)+x) * 3;
        rgbValues[px] = b;
        rgbValues[++px] = g;
        rgbValues[++px] = r;
    }
    (you were using the postfix++ operator when you should be using the ++prefix one).
    I don't live here any more.

  3. #3
    type Woss is new Grumpy; wossname's Avatar
    Join Date
    Aug 2002
    Location
    #!/bin/bash
    Posts
    5,682

    Re: I thought this might belong here..

    However...

    LockBits gives you a pointer to the raw pixel data of a bitmap. If you use pointer math then you can do things a HELL of a lot faster than passing loads of parameters around. I'll throw a quick demo together...
    I don't live here any more.

  4. #4
    type Woss is new Grumpy; wossname's Avatar
    Join Date
    Aug 2002
    Location
    #!/bin/bash
    Posts
    5,682

    Re: I thought this might belong here..

    OK I haven't had much time to bang this together but it demonstrates the simplest possible use of LockBits()...

    I've used a 32BPP image instead of your 24bit format, 32bits is very convenient because thats how long an int is in .Net.

    If you REALLY want to use 24 bpp then I can help you modify this demo to 24 bits but it does involve some extra fiddling (using structs).

    Lockbits and pointer arithmetic is faster than array addressing because pointers don't cause the compiler to do array bounds checks, the speed increase is definitely worth the extra complexity. But any ASM/C/C++ programmer would take this for granted anyway. Let me know if this helps.

    PS its in C# 2005.
    Attached Files Attached Files
    I don't live here any more.

  5. #5
    type Woss is new Grumpy; wossname's Avatar
    Join Date
    Aug 2002
    Location
    #!/bin/bash
    Posts
    5,682

    Re: I thought this might belong here..

    PPS, I haven't bothered to put in any safety checks, if you make the for() loop run for longer then you'll probably crash the app (it'll complain about memory corruption but don't worry its not harmful).
    I don't live here any more.

  6. #6

    Thread Starter
    G&G Moderator chemicalNova's Avatar
    Join Date
    Jun 2002
    Location
    Victoria, Australia
    Posts
    4,246

    Re: I thought this might belong here..

    Wow. I can't believe how incredibly helpful that is. Thanks heaps

    I do have one or two questions though:

    1) Using 32-bit, does that refer to the image being read in? Or does it simply refer to how the pointer can address the colors?

    2) Can you explain this just a tad?
    PHP Code:
    //calculate the X,Y coordinate and convert it into a int pointer address...
                    
    *(xy + (dy) + (dx)) = color//pointer math depends on what kind of pointers you use. 
    Thats pretty much a 2 part question. Number one being how do you calculate the x and y positions from that.. If I wanted to go straight across instead of diagonally...how would I? And number two being what do pointer maths depend on? Could you expand on that a little?

    Thanks heaps

    EDIT: Oh btw, I was using lockbits "kinda" the same way, just not using pointer math... This is how I was doing it:
    PHP Code:
                // GDI+ still lies to us - the return format is BGR, NOT RGB. 
                
    BitmapData bmData b.LockBits(new Rectangle(00b.Widthb.Height),
                    
    ImageLockMode.ReadWritePixelFormat.Format24bppRgb);
                
    // Get the address of the first line.
                
    IntPtr ptr bmData.Scan0;

                
    // Declare an array to hold the bytes of the bitmap.
                
    int numBytes b.Width b.Height 3;
                
    byte[] rgbValues = new byte[numBytes];

                
    // Copy the RGB values into the array.
                
    Marshal.Copy(ptrrgbValues0numBytes); 
    ..then I would edit the rgbValues array, then copy it back. That is actually taken directly from MSDN, so I can see now why it wouldn't be the fastest

    PPS: Sorry if my questions are completely newbish, I'm just so worn out from thinking about why it wasn't working my brain is dead.

    chem
    Last edited by chemicalNova; Oct 18th, 2006 at 08:06 AM.

    Visual Studio 6, Visual Studio.NET 2005, MASM

  7. #7
    type Woss is new Grumpy; wossname's Avatar
    Join Date
    Aug 2002
    Location
    #!/bin/bash
    Posts
    5,682

    Re: I thought this might belong here..

    1. The 32 bit pointer refers to the bitmap that I created (not the original bitmap which is just used to oad a little picture and then is thrown away). I then drew the original png image ONTO my 32 bit bitmap (buffer) (gr.DrawImageUnscaled. Now the 32bit image has pixel data in it. The int pointer means that if you add 1 to the pointer, the actual addres pointed to is 32 bits furter along, and not 1 byte like you might have imagined. This is how pointer arithmatic works.

    Since I created a 32bit bitmap then the raw data yielded by LockBits() will have 32 bits per pixel (one int per pixel). You can use a 24 bit image but you'd have to use a different pointer type (one that has a pointer increment of 24 bits (3 bytes) instead of 32 bits (4 bytes).

    2. In order to explain that calulation you need to properly understand how a bitmap's data is aligned in RAM. For a 32 bit image like I used, its as simple as can be. Scan0 refers to pixel(0,0) and the the whole top line of the image follows on in sequential bytes in RAM. When the top line is finished then the next line is streamed in right after it (without moving down one line).

    So if you want to fill the ENTIRE image with pixels of the same color all you'd need to do is simply loop from 0 to (w*y) a set each value to color, in RAM it would look like the pointer is moving RIGHT by 4 bytes each the time but on the image rectangle it would be striping left to right and then top to bottom.

    This may illustrate further...
    in my demo project change that calculation to...

    Code:
    *(xy + (i * dx)) = color;
    This will make the pointer simply step RIGHT by a single integer each time (one pixel at a time because its a 32bit image) BUT if you let <i> go up to, say, 500 then you will see that the int pointer wraps around the bitmap. experiment with the loop maximum and you will see what I mean.
    Last edited by wossname; Oct 18th, 2006 at 12:45 PM.
    I don't live here any more.

  8. #8
    type Woss is new Grumpy; wossname's Avatar
    Join Date
    Aug 2002
    Location
    #!/bin/bash
    Posts
    5,682

    Re: I thought this might belong here..

    PS, 24 bit images will have each horizontal row padded up to the nearest 4 byte boundary. This adds further complications because youare using pointers to non-intrinsic datatypes. Thus my example was in 32 bits. Doom# used 32bitPARGB pixel format (Premultiplied AlphaRedGreenBlue) because its the fastest and easiest format to address, it means you don't have to worry about 4 byte boundaries at all.

    PPS (again, sorry )
    re your comment: "// GDI+ still lies to us - the return format is BGR, NOT RGB.". Its not lying Intel is little-endian, meaning that in RAM a value like 0xAABBCCDD will come out as 0xDDCCBBAA if you read one byte at a time left to right. This is totally irrelevant though because int pointers already understand this anyway. An integer read from tha address woul have h value 0xAABBCCDD as you'd expect. If you used a byte array to store the data then yes it would look weird, but 24bit images are the work of the devil.

    I could go on all day about this but I'm sure I've given you more than enough to be getting on with.

    What does a guy have to do to get some reps round here? j/k
    I don't live here any more.

  9. #9

    Thread Starter
    G&G Moderator chemicalNova's Avatar
    Join Date
    Jun 2002
    Location
    Victoria, Australia
    Posts
    4,246

    Re: I thought this might belong here..

    Thank you very much I think I fully understand it now

    I think the problem with my first post was that, as you said, 24-bit images are the work of the devil. With your explanation, I'll be able to convert everything I had to use 32-bit, with alot less useless code.

    Having worked with single dimensional arrays for my tile engine, the calculations will be pretty simple now to reference a single pixel within a bitmap, now that I know I don't have to stuff around with the 24-bit stuff

    You've given me more than enough to get a move on indeed

    Thanks again

    chem

    Visual Studio 6, Visual Studio.NET 2005, MASM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width