CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 10 of 10
  1. #1
    Join Date
    Apr 2002
    Posts
    15

    Bitmap (Gdiplus) Pixel Format Conversion

    I am trying to save a GDI+ Bitmap to file.

    My main problem is getting the PixelFormat from 32 bpp PARGB to 1 bpp Indexed (I just want to save a black and white image) None of the GDI+ functions I'm using seem to work. I've tried to clone the 32 bpp image into a 1 bpp image.


    Bitmap * myBitmap = orgBitmap->Clone(orgBitmap->GetWidth(),orgBitmap->GetHeight(),PixelFormat1bppIndexed);


    myBitmap ends up being NULL;

    Also I've tried to use the Graphics function FromImage to do the conversion but that doesn't work either.


    Bitmap * newBitmap = new Bitmap(orgBitmap->GetWidth(),orgBitmap->GetHeight(),PixelFormat1bppIndexed);

    //Draw original Bitmap to new 1bpp bitmap
    Graphics * tmpGraphics = Graphics::FromImage(newBitmap);
    tmpGraphics->DrawImage(orgBitmap,(REAL)0,(REAL)0, orgBitmap->GetWidth(),tmpBitmap->GetHeight());


    tmpGraphics also ends up being NULL so any future code that uses the Graphics fails.

    Has anybody had any success with this?


    Thanks in advance,

    Greg

  2. #2
    Join Date
    Apr 2002
    Posts
    15

    Found this solution

    Found this on the microsoft.public.platformsdk.gdi
    by searching for PixelFormat1bppIndexed using Google Groups

    There appears to be some limitations with Indexed Image format in GdiPlus.

    Thanks to James DeBroeck (Microsoft Windows Developer Support) for the following solution.

    Version 1 of GdiPlus will not image format convert down to Indexed formats such as PixelFormat1bppIndexed.
    [Example: From PixelFormat24RGB to PixelFormat1bppIndexed.]

    The good news is that it can still be done. You will just have to implement the missing piece. GdiPlus does support creation and encoding/decoding of Indexed Bitmap formats. What it does not support is working directly on the Indexed image bits.

    What this means is that use of the Indexed formats is limited to creation, streaming, drawing and manipulation via the LockBits method. This is sufficient to achieve what you want to do.

    To save to a monochrome [image] file you will need to perform the following steps:

    1. Create and manipulate your image( in your case 24bpp ).
    2. Create a temporary monochrome image(PixelFormat1bppIndexed ).
    3. Use LockBits() to gain access to the pixel defintions of both Bitmaps.
    4. Copy the pixels from the PixelFormat24bppRGB Bitmap to the
    PixelFormat1bppIndexed Bitmap format via the Scan0 member of the BitmapData structure( returned by LockBits ).
    5. As you copy the pixels, apply your favorite color reduction algorithm to color reduce from 24bpp to 1bpp.
    6. Once you have color reduced to the temporary PixelFormat1bppIndexed Bitmap object, Use the paricular encoder to save the image [to desired image format - BMP, TIFF,etc...].

    I have appended some test code that does this to the end of this message.

    [This code converts from 24bpp to 1bpp]
    Bitmap *CopyImageToMono(Bitmap *pImage)
    {
    Bitmap *pMonoImage = new Bitmap(200, 200, PixelFormat1bppIndexed);
    Bitmap &bitmap = *pMonoImage;

    // Lock a rectangular portion of the bitmap for writing.
    BitmapData bitmapData;
    BitmapData monobitmapData;
    Rect rect(0, 0, 200, 200);

    ZeroMemory(&bitmapData, sizeof(bitmapData));
    ZeroMemory(&monobitmapData, sizeof(monobitmapData));

    bitmap.LockBits( // mono image
    &rect,
    ImageLockModeWrite,
    PixelFormat1bppIndexed,
    &monobitmapData);

    pImage->LockBits( // in image
    &rect,
    ImageLockModeRead,
    PixelFormat24bppRGB, // ask for 24bpp because we can only get a write lock if asking for 1bpp
    &bitmapData);

    // Write to the temporary buffer provided by LockBits.
    BYTE *pixelssource = (BYTE*)bitmapData.Scan0;
    BYTE *pixelsdest = (BYTE*)monobitmapData.Scan0;

    // Do the color demotion ourselves
    for(UINT row = 0; row < 200; row++)
    {
    BYTE bits;
    for(UINT col = 0; col < 200; col++)
    {
    // Reset the pixel location on byte boundary.
    if (col % 8 == 0)
    bits = 0x80;
    // Get source pixel
    RGBTRIPLE *prgbBit = (RGBTRIPLE *) &pixelssource[row
    *bitmapData.Stride];

    // Trivial (and largely incorrect) luminance test
    // A better test would probably be a least distance test
    between 0,0,0 and 255, 255, 255
    // that accounts for the visual nonlinearity of the RGB color
    space.

    if (prgbBit[col].rgbtBlue > 128 && prgbBit[col].rgbtGreen > 128
    && prgbBit[col].rgbtRed > 128)
    // if the 24bpp data says the pixel is "bright" write it into the destination.
    pixelsdest[row *monobitmapData.Stride + col/8] =
    pixelsdest[row *monobitmapData.Stride + col/8] | bits;

    // Next pixel location please
    bits = bits >> 1;
    }
    }

    // Commit the changes, and unlock the portion of the bitmap.
    bitmap.UnlockBits(&bitmapData);
    pImage->UnlockBits(&monobitmapData);

    return pMonoImage;

    }




    I used the above code and modified it to convert a 32bpp to 1bpp
    Here's my code:

    Bitmap *CopyImageToMono(Bitmap *pImage)
    {
    Bitmap *pMonoImage = new Bitmap(200, 200, PixelFormat1bppIndexed);
    Bitmap &bitmap = *pMonoImage;

    Bitmap& bitmap = *m_monoBitmap;

    BitmapData bitmapData;
    BitmapData monobitmapData;
    Rect rect(0,0,200,200);

    ZeroMemory(&bitmapData, sizeof(bitmapData));
    ZeroMemory(&monobitmapData, sizeof(monobitmapData));

    bitmap.LockBits( // mono image
    &rect,
    ImageLockModeWrite,
    PixelFormat1bppIndexed,
    &monobitmapData);

    pImage->LockBits( // in image
    &rect,
    ImageLockModeRead,
    PixelFormat32bppPARGB, //32bpp instead of 24bpp
    &bitmapData);

    // Write to the temporary buffer provided by LockBits.
    BYTE *pixelssource = (BYTE*)bitmapData.Scan0;
    BYTE *pixelsdest = (BYTE*)monobitmapData.Scan0;

    // Do the color demotion ourselves
    for(UINT row = 0; row < 200; row++)
    {
    BYTE bits;
    for(UINT col = 0; col < 200; col++)
    {
    // Reset the pixel location on byte boundary.
    if (col % 8 == 0)
    bits = 0x80;
    // Get source pixel
    UINT *prgbBit = (UINT *) &pixelssource[row*bitmapData.Stride];

    //when the 1bpp bitmap is created all bits are zero (black)
    //If the source bit is white then set the destination bit (in the 1bpp bitmap to white)

    if(prgbBit[col] == (UINT) 0xffffffff)
    pixelsdest[row *monobitmapData.Stride + col/8] = pixelsdest[row *monobitmapData.Stride + col/8] | bits;

    // Next pixel location please
    bits = bits >> 1;
    }
    }

    // Commit the changes, and unlock the portion of the bitmap.
    bitmap.UnlockBits(&bitmapData);
    m_pImage->UnlockBits(&monobitmapData);

    return pMonoImage;

    }



    Once you have the 1bpp Bitmap you can use GetEncoderClsid() see MSDN for help in order to save to disk.

    MSDN : GDI+
    Using Image Encoders and Decoders
    http://msdn.microsoft.com/library/de...odecs_0dnp.asp


    Hope this helps.

    Greg

  3. #3
    Join Date
    Jul 2000
    Location
    In your a.. , deep in its right.
    Posts
    170

    what about 24bpp to 8bpp?

    Hi,

    Using GDI+, how to convert a 24bpp GDI+ bitmap to an 8bpp GDI+ Bitmap?

    I'd like the same algorith but to convert 24bpp to 8bpp Bitmap.

    Thanks.

    Bouli.

  4. #4
    Join Date
    Apr 2002
    Posts
    15
    Have you tried to use the Clone function with PixelFormat8bppIndexed as the Pixel Format?

    Check out :

    http://msdn.microsoft.com/library/en...asp?frame=true

    for more info on Bitmap Clone function.





    Greg

  5. #5
    Join Date
    Jul 2000
    Location
    In your a.. , deep in its right.
    Posts
    170

    Hi

    Hi greg,

    Thanks for reply.

    I've found the way how to convert 24bpp to 8bppIndexed bitmaps using GDI+. It's much much more complex than using the clone method. The clone method does not work anyway...

    The trick is that you need to do the following things:
    - 1) create a pallette with corresponding colors in 24bpp (with alpha channel set to 00 insted of FF) or copy the palette from the source to the destination's palette's bitmap.
    - 2) create your destination bitmap in the correct color depth format ie: bmpDest(10, 10, PixelFormat8bppIndexed); and set the palette on the destination bitmap
    - 3) for each pixels of the source bitmap, get the color of the pixel;
    - 4) once you have the color of the pixel from the source bitmap, search for the nearest color corresponding to the palette's destination bitmaps' index. (It's the hardest part), use cubic search algorithm.
    - 5) write the index in the destination bitmap's data.
    - 6) save the destination bitmap using Save method...

    Not easy isn't it?

    GDI+ 1.1 provides the conversion method... Just wait and see.

    Thanks anyway.

    Best regards.

    Bouli.

  6. #6
    Join Date
    Jan 2003
    Location
    Paris, France
    Posts
    29
    Hi boulifb,

    I've got exactly the same problem as yours and i came to the same conclusion... We need to create or import a palette and then for each pixel, find the nearest color in this palette.

    However, i feel my search algorithm quite slow and i wonder if you wish to share yours or help me to do an efficient algorithm.

    Thanks in advance for any answer

    regards
    Stephane Debusne
    Cynove

  7. #7
    Join Date
    Jul 2000
    Location
    In your a.. , deep in its right.
    Posts
    170

    color reduction algorythm

    Hi Stephane,

    I used the algorithm taken from the following artcile:
    http://www.newlc.com/article.php3?id_article=45

    it describes in standard GDI how to do color reduction depth.

    you'll need to adapt it to GDI+.
    Good luck.

    boulifb.

  8. #8
    Join Date
    Jan 2003
    Location
    Paris, France
    Posts
    29
    thanks for the link :-)
    Stephane Debusne
    Cynove

  9. #9
    Join Date
    Jul 2000
    Location
    In your a.. , deep in its right.
    Posts
    170

    y a pas de quoi...

    de rien...

    bonne chance, c'est ballot de transformer ce code en GDI+...

    @+

    bouilfb.

  10. #10
    Join Date
    Jul 2008
    Posts
    1

    Re: Bitmap (Gdiplus) Pixel Format Conversion

    Edit: I just realised how insanely out-of-date this post is, I'm not sure if they've even made a function that does this for you, though if they have it might not have the same functionality as this does, either way, you could build on this and make all sorts of dithering patterns for different shades.



    I thought some might find this useful, though I haven't tested it, it should work perfectly.

    This code will convert a color into monochrome using the brightness of the color, and will also make any mid-tones alternate between black and white dependant on the pixel.

    To check whether to use black or white:

    if ((r+g+b)/765 < 0.33) {
    //Black
    } else if((r+g+b)/765 < 0.67) {
    if (I % 2 == 1) {
    //Black
    } else {
    //White
    }
    } else {
    //White
    }

    Where I is the index of the pixel. If you're scanning the columns/rows, then you would instead write:

    if (X + (Y % 2)) % 2 == 1) {

    Otherwise if you simply asked if (X % 2 == 1) you would get a bunch of vertical lines instead of a checker pattern.

    If anyone tries this let me know if it works.
    Last edited by Nimphious; July 19th, 2008 at 09:59 AM.

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

Featured