Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if Gdiplus::Bitmap::FromFile has returned a valid Bitmap

Tags:

c++

gdi+

I had assumed that Gdiplus::Bitmap::FromFile returns NULL if the file is not a valid image, but it returns a non-NULL even if I pass it a doc file, for example. There doesn't seem to be an IsValid method for Bitmap, or anything similar.

So how do I know if Gdiplus::Bitmap::FromFile has really loaded a valid image?

like image 685
sashoalm Avatar asked Jul 05 '12 13:07

sashoalm


2 Answers

It turns out that Bitmap::GetLastStatus() is what I was looking for, it returns Gdiplus::Ok if the load operation succeded, or an error code if it failed.

like image 113
sashoalm Avatar answered Nov 09 '22 03:11

sashoalm


The following code shows the usage of Gdiplus to load a bitmap from a file. First part is a generic sample code and shows how to enable Gdiplus and check if the bitmap is available in memory.If Gdiplus is not enabled Gdiplus::Status __retVal = bitmap->GetLastStatus(); results in an 'Out of memory' error, if the file can't be found or something else goes wrong the error code is different, see the possible error codes in the enum Status of Gdiplus. With using namespace Gdiplus there is no need for 'Gdiplus::' prefix however the sample code uses the prefix for a better understanding. The line #pragma comment(lib, "Gdiplus.lib") is probably only effective in VisualStudio, in cases where other compilers are used the Gdiplus.lib may have to be explicitly added to the linker options


//------------------------------------------------------------
//Before using anything from Gdiplus initialization is needed:
//------------------------------------------------------------

#include <GdiPlus.h>
using namespace Gdiplus;
#pragma comment(lib, "Gdiplus.lib")


GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR           gdiplusToken;

GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

Gdiplus::Bitmap* image = Gdiplus::Bitmap::FromFile(filename);

if (image == NULL)
return FALSE;
if (Gdiplus::Ok != image->GetLastStatus())
return FALSE;

//bitmap successfully loaded...
//place specific code here to process the bitmap/bitmap data

GdiplusShutdown(gdiplusToken);

Second part is pret-a-porter code and shows a concrete implementation of reading a bitmap file into memory with Gdiplus.The r, g, b, a values of the bitmap's byte buffer are read into a vector of type byte so later they can be used for e.g. an OpenGL GL_RGBA texture. Bitmaps of type BMP, GIF, JPEG, PNG and some more image types can be read


//--------------------------------------------------------------------------------
//The following example code loads me a .jpeg file into a byte buffer with r,g,b,a 
//--------------------------------------------------------------------------------
#pragma comment(lib, "Gdiplus.lib")

#include <Unknwn.h>    
#include <windows.h>
#include <windowsx.h>
#include <objidl.h>
#include <Gdiplus.h> 
#include <Gdiplusheaders.h> 
#include <Gdipluscolor.h>
#include <Gdiplusflat.h>
#include <wchar.h>
#include <vector>

bool Texture::readFromFile(const wchar_t* filename)
{
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;

    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    bool bRetVal = false;
    // Create a Bitmap object from a JPEG file.
    Gdiplus::Bitmap* bitmap = 0;

    int wt, ht;
    Gdiplus::BitmapData bitmapData;


    bitmap = Gdiplus::Bitmap::FromFile(filename);
    Gdiplus::Status __retVal = bitmap->GetLastStatus();
    if (__retVal == Gdiplus::Status::Ok)
    {
        wt = bitmap->GetWidth();//get width of image
        ht = bitmap->GetHeight();//get height of image
        Gdiplus::Rect rect(0, 0, wt, ht);//create a rect object


        std::vector<GLubyte> dstBuffer = std::vector<GLubyte>(wt * ht * 4);
        //Glubyte is OpenGL unsigned byte, 4 bytes per pixel for RGBA 8-bit per channel
        //so dstBuffer is a continuous byte buffer, destination for the bytes read from the bitmap
        //this is needed for eventually reordering the bytes as e.g. OpenGL textures have a 
        //specific format which may not match the layout of the source bitmap

        bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat32bppRGB, &bitmapData);
        //lock bitmap in memory: so it can be (1) read or written to,and (2) CONVERT to a target format, here PixelFormat32bppRGB
        //use target format to be sure how many bytes you have (to read) per pixel, original bitmap may be only RGB
        //where you have only 3 bytes per pixel or some other format
        //LockBits fills the given 'bitmapData' structure...
        const uint8_t* srcDataBase = (uint8_t*)bitmapData.Scan0;
        //...from which you get a 'source' pointer to the bitmap bytes, here it is a byte pointer

        //now loop through the pixel data (outer loop is image height, inner loop is width)
        //if the byte layout of the bitmap's byte buffer is already in the correct order
        //it may seem redundant to loop through it, but still you may have the stride problem
        //if your picture width is not divisible by 4 (or divisble by 8 on a 64bit architecture)
        //but WINDOWS aligns it on a DWORD (32bit) or QWORD (64bit)
        int i = 0;//destination buffer is contiguous, so index i is increased per byte (pixel = 4 byte)
        for (int y = 0; y < ht; y++)
        {
            for (int x = 0; x < wt; x++)
            {
                //x << 2 is the same as x*4 but should be much faster, the x index 
                //has to be multiplied by 4 as format is PixelFormat32bppRGB so
                //we have 4 byte values per pixel: r,g,b,a (or a different order)
                dstBuffer[i++] = *(srcDataBase + 2 + (x << 2));
                dstBuffer[i++] = *(srcDataBase + 1 + (x << 2));
                dstBuffer[i++] = *(srcDataBase + 0 + (x << 2));
                dstBuffer[i++] = *(srcDataBase + 3 + (x << 2));
                //the above 4 lines transfer ONE pixel (=4 byte values) from source to destination
                //(dst = dstBuffer, src = srcDataBase)
                //note that the order of the input bytes is not the same as the order of
                //the output bytes written, this is necessary if e.g. the
                //source bitmap has a,r,g,b format and destination bitmap format has order b,g,r,a etc.
                //code could be optimized: a bitmap with a width divisible by four
                //should be read from source or at least written to destination buffer in an int IO per pixel
            }
            srcDataBase += bitmapData.Stride;
            //a bitmap's horizontal pixel data (=bytes) for one row are contiguous however bitmaps internally
            //and for efficency reasons may be aligned on 4-even memory adresses (or other even multiples) 
            //but if your bitmap width is indivisible by 4,  the bitmap's pointer 
            //may be non-continuous between horizontal pixel rows: 
            //no simple 4 byte increase between the last pixel of row and the first pixel of row+1, 
            //instead stride has to be added to the source pointer to point correctly to
            //the first pixel of the next row
            //this is what bitmapData.Stride accounts for
            //if you read an int (int = 4 bytes = a word) and it is not word aligned (=adress is divisible by 4)
            //you probably need 2 read operations to access the memory for the complete int and all successive ints of an int buffer
            //if it IS word aligned only one read OP (per buffer element) is needed (on a 64bit architecture a QWORD IO is most efficient)
        }
        bitmap->UnlockBits(&bitmapData);
        //dont forget to unlock the bitmap in memory so storage can be optimized by OS
        //does not destroy it, can be locked again

        //
        //...OpenGL code using dstBuffer is omitted here...
        //
        bRetVal = true;
    }
    Gdiplus::GdiplusShutdown(gdiplusToken);
    return bRetVal;
}

like image 22
Robert S. Avatar answered Nov 09 '22 03:11

Robert S.