Get bitmap from .DLL, convert to byte[] and to image

CularBytes

I have received an .DLL for testing purposes, this .dll includes the functionality that will later be used to process live images from hardware.

For this simple .dll, I can open an image (load into memory), get the width and height, and get the pixels which need to be converted to a image. Loading, getting width and getting height is fine, but getting the pixels and converting that to a Bitmap or Image is a problem.

C++ example source that I received:

   ApiFunc->OpenImageFile(this->OpenPictureDialog1->FileName.c_str());
   ApiFunc->AllocMemory();

   w = ApiFunc->GetImageWidth();
   h = ApiFunc->GetImageHeight();
   Image1->Width = w;
   Image1->Height = h;

   unsigned char* ptr = ApiFunc->GetImagePixels();
   COLORREF pixel;
   int r,g,b;
   for(int y=0; y<w; y++)
   {
       for(int x=0; x<h; x++)
       {
           r = (int)*(ptr+3*x);
           g = (int)*(ptr+3*x+1);
           b = (int)*(ptr+3*x+2);

           Image1->Canvas->Pixels[y][x] = RGB(r,g,b);
       }
       ptr += 3*h;
   }

Where in ApiFunc, this can be found:

void __fastcall TAPIFunc::LoadDll(HINSTANCE m_hMain)
{
   //some others above
   GET_IMAGE_PIXELS  = (func_GET_IMAGE_PIXELS   )GetProcAddress( m_hMain, "GET_IMAGE_PIXELS");
   //some others below
}
unsigned char* __fastcall TAPIFunc::GetImagePixels(void)
{
   return GET_IMAGE_PIXELS();
}

So now what I have tried so far, I've tried using byte[] as return parameter, but that throw an MarshalDirectiveException.

    [DllImport("ImageTest.dll")]
    public static extern IntPtr GET_IMAGE_PIXELS();

    private void OpenImage(string filename)
    {
        OPEN_IMAGE_FILE(filename);
        ALLOC_MEMORY();
        int width = GET_IMAGE_WIDTH(); //=800
        int height = GET_IMAGE_HEIGHT(); //=600
        IntPtr buffer = GET_IMAGE_PIXELS();
        int size = width * height * 3;//not sure what the size must be, I think this is one of the issues, just following logic of one answer below.

        //but source: https://stackoverflow.com/a/16300450/2901207
        byte[] bitmapImageArray = new byte[size];
        Marshal.Copy(buffer, bitmapImageArray, 0, size);

        Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);
        BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
        IntPtr pNative = bmData.Scan0;
        Marshal.Copy(imageData, 0, pNative,size);
        bitmap.UnlockBits(bmData);
        bitmap.Save(Environment.CurrentDirectory + @"\result.bmp");
            using (var ms = new MemoryStream(bitmapImageArray))
            {
                //both throw exception: Parameter is not valid
                Bitmap bmp = new Bitmap(ms);
                Image bitmapImage = Image.FromStream(ms);
            }
    }

answer sources:

answer 1, using bitmap.LockBits method answers 2 and 3, using memoryStream

Just to make sure I have a valid image to test with, I saved an image in photoshop, with this option: photoshoppie

ugly test image:

rotated example image

result: awesome result

Beautiful isn't it? :)

Also tried using a for-loop, and run until it crashes. ran until count = 1441777 and on another image count = 1527793 (same dimensions).

        int count = 0;
        for (int i = 0; i < width * height * 4; i++)
        {
            count++;
            bitmapImageArray[i] = Marshal.ReadByte(buffer, i);
        }
dim

It appears from the wrong result that the horizontal/vertical is actually switched. Your image is obtained with pixels organized columns by columns, instead of rows by rows (as usually done).

This is confirmed by the example source you received: the outer loop goes to w (width) and the inner loop goes to h (height), although the outer variable is y and the inner variable is x, which is confusing.

It also appears that the R and B components are switched (I don't have the explanation, here, but trust me).

Therefore, after having obtained the array using

byte[] bitmapImageArray = new byte[size]; Marshal.Copy(buffer, bitmapImageArray, 0, size);

You must reorganize it. allocate another buffer bitmapImageArray2 of the same size, loop over all pixels (row by row, or column by column, as you prefer, but with correct naming of the variables unlike the example : x goes to w and y goes to h), and write it to the destination array like that:

bitmapImageArray2[(y * w + x) * 3] = bitmapImageArray[(x * h + y) * 3 + 2];
bitmapImageArray2[(y * w + x) * 3 + 1] = bitmapImageArray[(x * h + y) * 3 + 1];
bitmapImageArray2[(y * w + x) * 3 + 2] = bitmapImageArray[(x * h + y) * 3];

Note: your value for size seems to be correct.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related