7

我假设如果文件不是有效图像,Gdiplus::Bitmap::FromFile 返回 NULL,但即使我将它传递给 doc 文件,它也会返回非 NULL。Bitmap 或类似的东西似乎没有 IsValid 方法。

那么我怎么知道 Gdiplus::Bitmap::FromFile 是否真的加载了一个有效的图像呢?

4

2 回答 2

11

事实证明这Bitmap::GetLastStatus()就是我正在寻找的,Gdiplus::Ok如果加载操作成功则返回,如果失败则返回错误代码。

于 2012-07-19T13:48:39.207 回答
1

以下代码显示了 Gdiplus 从文件中加载位图的用法。第一部分是一个通用示例代码,展示了如何启用 Gdiplus 并检查位图是否在内存中可用。如果未启用 Gdiplus 会 Gdiplus::Status __retVal = bitmap->GetLastStatus(); 导致“内存不足”错误,如果找不到文件或其他问题错误的错误代码不同,请参阅enum StatusGdiplus中可能的错误代码。using namespace Gdiplus不需要 'Gdiplus::' 前缀,但示例代码使用前缀以便更好地理解。该行#pragma comment(lib, "Gdiplus.lib")可能仅在 VisualStudio 中有效,在使用其他编译器的情况下,可能必须将 Gdiplus.lib 显式添加到链接器选项中


//------------------------------------------------------------
//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);

第二部分是 pret-a-porter 代码,展示了使用 Gdiplus 将位图文件读入内存的具体实现。位图字节缓冲区的 r、g、b、a 值被读入 byte 类型的向量,以便稍后它们可以用于例如 OpenGL GL_RGBA 纹理。可以读取 BMP、GIF、JPEG、PNG 和更多图像类型的位图


//--------------------------------------------------------------------------------
//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;
}

于 2021-01-17T16:56:47.903 回答