我曾以为 Gdiplus::Bitmap::FromFile 如果文件不是有效的图像,则返回NULL,但即使我传递一个doc文件,它也会返回非NULL。 Bitmap没有IsValid方法或类似的方法。
那么,我如何知道 Gdiplus::Bitmap::FromFile 是否真正加载了有效的图像?
我曾以为 Gdiplus::Bitmap::FromFile 如果文件不是有效的图像,则返回NULL,但即使我传递一个doc文件,它也会返回非NULL。 Bitmap没有IsValid方法或类似的方法。
那么,我如何知道 Gdiplus::Bitmap::FromFile 是否真正加载了有效的图像?
事实证明,我正在寻找的是 Bitmap::GetLastStatus()
,它返回 Gdiplus::Ok
如果加载操作成功,否则返回错误代码。
Gdiplus :: Status __retVal = bitmap->GetLastStatus();
会导致“内存不足”错误,如果找不到文件或出现其他问题,则错误代码是不同的,请参见Gdiplus的 enum Status
中可能的错误代码。使用 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);
第二部分是时装代码,展示了使用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;
}