I currently have the following details about an image
int nBufSize ; //contains the buffer size
void* lpBmpBuf; //The pointer to the first byte of the bitmap buffer
How can I obtain a QPixmap from this ?
Here is what I am doing so far
QByteArray b((char*)lpBmpBuf,nBufSize);
bool t = pix.loadFromData(b, 0, Qt::AutoColor);
However t is false in this case. Any suggestions ?
Copy the bitmap buffer into a byte array as you also have the length, then:
QPixmap::loadFromData(&data, 0, Qt::AutoColor);
data is the QByteArray in my example.
Also if you know the extension/type of the file you can specify it in the 2nd argument:
loadFromData(&data, "BMP");
Related
In a camera application bitmap pixel arrays are retrieved from a streaming camera.
The pixel arrays are captured by writing them to a named pipe, where on the other end of the pipe, ffmpeg retrieves them and creates an AVI file.
I will need to create one custom frame (with custom text on), and pipe its pixels as the first frame in the resulting movie.
The question is how can I use a TBitmap (for convenience) to
Create a X by Y monochrome (8 bit) bitmap from scratch, with
custom text on. I want the background to be white, and the text to
be black. (Mostly figured this step out, see below.)
Retrieve the pixel array that I can send/write to the pipe
Step 1: The following code creates a TBitmap and writes text on it:
int w = 658;
int h = 492;
TBitmap* bm = new TBitmap();
bm->Width = w;
bm->Height = h;
bm->HandleType = bmDIB;
bm->PixelFormat = pf8bit;
bm->Canvas->Font->Name = "Tahoma";
bm->Canvas->Font->Size = 8;
int textY = 10;
string info("some Text");
bm->Canvas->TextOut(10, textY, info.c_str());
The above basically concludes step 1.
The writing/piping code expects a byte array with the bitmaps pixels; e.g.
unsigned long numWritten;
WriteFile(mPipeHandle, pImage, size, &numWritten, NULL);
where pImage is a pointer to a unsigned char buffer (the bitmaps pixels), and the size is the length of this buffer.
Update:
Using the generated TBitmap and a TMemoryStream for transferring data to the ffmpeg pipeline does not generate the proper result. I get a distorted image with 3 diagonal lines on it.
The buffersize for the camera frame buffers that I receive are are exactly 323736, which is equal to the number of pixels in the image, i.e. 658x492.
NOTE I have concluded that this 'bitmap' is not padded. 658 is not divisible by four.
The buffersize I get after dumping my generated bitmap to a memory stream, however, has the size 325798, which is 2062 bytes larger than it is supposed to be. As #Spektre pointed out below, this discrepancy may be padding?
Using the following code for getting the pixel array;
ByteBuffer CustomBitmap::getPixArray()
{
// --- Local variables --- //
unsigned int iInfoHeaderSize=0;
unsigned int iImageSize=0;
BITMAPINFO *pBitmapInfoHeader;
unsigned char *pBitmapImageBits;
// First we call GetDIBSizes() to determine the amount of
// memory that must be allocated before calling GetDIB()
// NB: GetDIBSizes() is a part of the VCL.
GetDIBSizes(mTheBitmap->Handle,
iInfoHeaderSize,
iImageSize);
// Next we allocate memory according to the information
// returned by GetDIBSizes()
pBitmapInfoHeader = new BITMAPINFO[iInfoHeaderSize];
pBitmapImageBits = new unsigned char[iImageSize];
// Call GetDIB() to convert a device dependent bitmap into a
// Device Independent Bitmap (a DIB).
// NB: GetDIB() is a part of the VCL.
GetDIB(mTheBitmap->Handle,
mTheBitmap->Palette,
pBitmapInfoHeader,
pBitmapImageBits);
delete []pBitmapInfoHeader;
ByteBuffer buf;
buf.buffer = pBitmapImageBits;
buf.size = iImageSize;
return buf;
}
So final challenge seem to be to get a bytearray that has the same size as the ones coming from the camera. How to find and remove the padding bytes from the TBitmap code??
TBitmap has a PixelFormat property to set the bit depth.
TBitmap has a HandleType property to control whether a DDB or a DIB is created. DIB is the default.
Since you are passing BMPs around between different systems, you really should be using DIBs instead of DDBs, to avoid any corruption/misinterpretation of the pixel data.
Also, this line of code:
Image1->Picture->Bitmap->Handle = bm->Handle;
Should be changed to this instead:
Image1->Picture->Bitmap->Assign(bm);
// or:
// Image1->Picture->Bitmap = bm;
Or this:
Image1->Picture->Assign(bm);
Either way, don't forget to delete bm; afterwards, since the TPicture makes a copy of the input TBitmap, it does not take ownership.
To get the BMP data as a buffer of bytes, you can use the TBitmap::SaveToStream() method, saving to a TMemoryStream. Or, if you just want the pixel data, not the complete BMP data (ie, without BMP headers - see Bitmap Storage), you can use the Win32 GetDiBits() function, which outputs the pixels in DIB format. You can't obtain a byte buffer of the pixels for a DDB, since they depend on the device they are rendered to. DDBs are only usable in-memory in conjunction with HDCs, you can't pass them around. But you can convert a DIB to a DDB once you have a final device to render it to.
In other words, get the pixels from the camera, save them to a DIB, pass that around as needed (ie, over the pipe), and then do whatever you need with it - save to a file, convert to DDB to render onscreen, etc.
This is just an addon to existing answer (with additional info after the OP edit)
Bitmap file-format has align bytes on each row (so there usually are some bytes at the end of each line that are not pixels) up to some ByteLength (present in bmp header). Those create the skew and diagonal like lines. In your case the size discrepancy is 4 bytes per row:
(xs + align)*ys + header = size
(658+ 4)*492 + 94 = 325798
but beware the align size depends on image width and bmp header ...
Try this instead:
// create bmp
Graphics::TBitmap *bmp=new Graphics::TBitmap;
// bmp->Assign(???); // a) copy image from ???
bmp->SetSize(658,492); // b) in case you use Assign do not change resolution
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf8bit;
// bmp->Canvas->Draw(0,0,???); // b) copy image from ???
// here render your text using
bmp->Canvas->Brush->Style=bsSolid;
bmp->Canvas->Brush->Color=clWhite;
bmp->Canvas->Font->Color=clBlack;
bmp->Canvas->Font->Name = "Tahoma";
bmp->Canvas->Font->Size = 8;
bmp->Canvas->TextOutA(5,5,"Text");
// Byte data
for (int y=0;y<bmp->Height;y++)
{
BYTE *p=(BYTE*)bmp->ScanLine[y]; // pf8bit -> BYTE*
// here send/write/store ... bmp->Width bytes from p[]
}
// Canvas->Draw(0,0,bmp); // just renfder it on Form
delete bmp; bmp=NULL;
mixing GDI winapi calls for pixel array access (bitblt etc...) with VCL bmDIB bitmap might cause problems and resource leaks (hence the error on exit) and its also slower then usage of ScanLine[] (if coded right) so I strongly advice to use native VCL functions (as I did in above example) instead of the GDI/winapi calls where you can.
for more info see:
#4. GDI Bitmap
Delphi / C++ builder Windows 10 1709 bitmap operations extremely slow
Draw tbitmap with scale and alpha channel faster
Also you mention your image source is camera. If you use pf8bit it mean its palette indexed color which is relatively slow and ugly if native GDI algo is used (to convert from true/hi color camera image) for better transform see:
Effective gif/image color quantization?
simple dithering
Is there a way to save an image as "text" and then read it back? Indeed I'm using Qt and I'm trying to save/read a base64-encoded representation of a pixmap but nothing works as expected. Here is my current implementation sprinkled with several comments:
/*
* the culprit pixmap
*/
QPixmap pixmap = QPixmap(a_given_jpg_filepath);
/*
* pixmap to array of bytes
*/
QByteArray bytes;
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
pixmap.save(&buffer, "jpg"); // writes pixmap into bytes in jpg format
/*
* byte array to text
* this string is saved to xml (among others) and then read back
*/
const QString &str(bytes.toBase64()); // a base64-encoded version of the pixmap
/*
* now trying to construct the pixmap (having it encoded as base64)
*/
const auto wantConversion = true;
if(wantConversion) {
const QByteArray &bytes = QByteArray::fromBase64(str.toLatin1());
QPixmap(bytes); // this pixmap is not the same as the previous one
}
What am I doing wrong? Am I on the right route to the solution? Any fix/remark is welcome.
You need to call loadFromData()
const QByteArray &bytes = QByteArray::fromBase64(str.toLatin1());
QPixmap fromBase64;
fromBase64.loadFromData(bytes);
I'm trying to retrieve the pixel information for an alpha-only texture via glGetTexImage.
The problem is, the glGetTexImage-Call seems to read more data than it should, leading to memory corruption and a crash at the delete[]-Call. Here's my code:
int format;
glGetTexLevelParameteriv(target,0,GL_TEXTURE_INTERNAL_FORMAT,&format);
int w;
int h;
glGetTexLevelParameteriv(target,0,GL_TEXTURE_WIDTH,&w);
glGetTexLevelParameteriv(target,0,GL_TEXTURE_HEIGHT,&h);
if(w == 0 || h == 0)
return false;
if(format != GL_ALPHA)
return false;
unsigned int size = w *h *sizeof(unsigned char);
unsigned char *pixels = new unsigned char[size];
glGetTexImage(target,level,format,GL_UNSIGNED_BYTE,&pixels[0]);
delete[] pixels;
glGetError reports no errors, and without the glGetTexImage-Call it doesn't crash.
'target' is GL_TEXTURE_2D (The texture is valid and bound before the shown code), 'w' is 19, 'h' is 24, 'level' is 0.
If I increase the array size to (w *h *100) it doesn't crash either. I know for a fact that GL_UNSIGNED_BYTE has the same size as an unsigned char on my system, so I don't understand what's going on here.
Where's the additional data coming from and how can I make sure that my array is large enough?
Each row written to or read from by OpenGL pixel operations like glGetTexImage are aligned to a 4-byte boundary by default, which may add some padding.
To modify the alignment, use glPixelStorei with the GL_[UN]PACK_ALIGNMENT setting. GL_PACK_ALIGNMENT affects operations that read from OpenGL memory (glReadPixels, glGetTexImage, etc.) while GL_UNPACK_ALIGNMENT affects operations that write to OpenGL memory (glTexImage, etc.)
The alignment can be any of 1 (tightly packed with no padding), 2, 4 (the default), or 8.
So in your case, run glPixelStorei(GL_PACK_ALIGNMENT, 1); before running glGetImage2D.
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?
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.
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;
}
For some reason i can't figure i am getting access violation.
memcpy_s (buffer, bytes_per_line * height, image, bytes_per_line * height);
This is whole function:
int Flip_Bitmap(UCHAR *image, int bytes_per_line, int height)
{
// this function is used to flip bottom-up .BMP images
UCHAR *buffer; // used to perform the image processing
int index; // looping index
// allocate the temporary buffer
if (!(buffer = (UCHAR *) malloc (bytes_per_line * height)))
return(0);
// copy image to work area
//memcpy(buffer, image, bytes_per_line * height);
memcpy_s (buffer, bytes_per_line * height, image, bytes_per_line * height);
// flip vertically
for (index = 0; index < height; index++)
memcpy(&image[((height - 1) - index) * bytes_per_line], &buffer[index * bytes_per_line], bytes_per_line);
// release the memory
free(buffer);
// return success
return(1);
} // end Flip_Bitmap
Whole code:
http://pastebin.com/udRqgCfU
To run this you'll need 24-bit bitmap, in your source directory.
This is a part of a larger code, i am trying to make Load_Bitmap_File function to work...
So, any ideas?
You're getting an access violation because a lot of image programs don't set biSizeImage properly. The image you're using probably has biSizeImage set to 0, so you're not allocating any memory for the image data (in reality, you're probably allocating 4-16 bytes, since most malloc implementations will return a non-NULL value even when the requested allocation size is 0). So, when you go to copy the data, you're reading past the ends of that array, which results in the access violation.
Ignore the biSizeImage parameter and compute the image size yourself. Keep in mind that the size of each scan line must be a multiple of 4 bytes, so you need to round up:
// Pseudocode
#define ROUNDUP(value, power_of_2) (((value) + (power_of_2) - 1) & (~((power_of_2) - 1)))
bytes_per_line = ROUNDUP(width * bits_per_pixel/8, 4)
image_size = bytes_per_line * height;
Then just use the same image size for reading in the image data and for flipping it.
As the comments have said, the image data is not necessarily width*height*bytes_per_pixel
Memory access is generally faster on 32bit boundaries and when dealing with images speed generally matters. Because of this the rows of an image are often shifted to start on a 4byte (32bit) boundary
If the image pixels are 32bit (ie RGBA) this isn't a problem but if you have 3bytes per pixel (24bit colour) then for certain image widths, where the number of columns * 3 isn't a multiple of 4, then extra blank bytes will be inserted at the edn of each row.
The image format probably has a "stride" width or elemsize value to tell you this.
You allocate bitmap->bitmapinfoheader.biSizeImage for image but proceed to copy bitmap->bitmapinfoheader.biWidth * (bitmap->bitmapinfoheader.biBitCount / 8) * bitmap->bitmapinfoheader.biHeight bytes of data. I bet the two numbers aren't the same.