我假设如果文件不是有效图像,Gdiplus::Bitmap::FromFile 返回 NULL,但即使我将它传递给 doc 文件,它也会返回非 NULL。Bitmap 或类似的东西似乎没有 IsValid 方法。
那么我怎么知道 Gdiplus::Bitmap::FromFile 是否真的加载了一个有效的图像呢?
事实证明这Bitmap::GetLastStatus()
就是我正在寻找的,Gdiplus::Ok
如果加载操作成功则返回,如果失败则返回错误代码。
以下代码显示了 Gdiplus 从文件中加载位图的用法。第一部分是一个通用示例代码,展示了如何启用 Gdiplus 并检查位图是否在内存中可用。如果未启用 Gdiplus 会
Gdiplus::Status __retVal = bitmap->GetLastStatus();
导致“内存不足”错误,如果找不到文件或其他问题错误的错误代码不同,请参阅enum Status
Gdiplus中可能的错误代码。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;
}