I have an application where I copy some raw image data into a QImage directly:
QImage* img = new QImage(desc.Width, desc.Height, QImage::Format_RGB32);
for (y = 0; y < img->height(); y++)
{
memcpy(img->scanLine(y), &rawData[y * pRes->RowPitch], pRes->RowPitch);
}
return img;
Later this QImage is drawn via a call
painter.drawPixmap();
Unfortunately drawPixmap() cannot handle a QImage directly, so it first has to be converted:
m_bgImage = new QPixmap();
m_bgImage->convertFromImage(image);
Due to timing reasons I would like to drop this additional conversion step.
Thus my question: are there any function in QPixmap that allow direct image data manipulation right as in QImage?
My idea would be to start with a QPixmap from the very beginning, copy the raw image data into the QPixmap object and then use it directly.
Thanks :-)
First of all you won't need that loop to create the QImage. You can:
QImage* img = new QImage(&rawData, desc.Width, desc.Height, pRes->RowPitch * 4, QImage::Format_RGB32);
Then you can
painter.drawImage(QPointF(0,0),*img);
If there is any specific reason to use QPixmap (like QPixmap caching) you will have no other choice than convert it to QPixmap first.
I'm new to graphics programming (pixels, images, etc..)
I'm trying to convert Raw data to QImage and display it on QLabel. The problem is that, the raw data can be any data (it's not actually image raw data, it's binary file.)
The reason if this is that, to understand deeply how pixels and things like that work, I know I'll get random image with weird results, but it will work.
I'm doing something like this, but I think I'm doing it wrong!
QImage *img = new QImage(640, 480, QImage::Format_RGB16); //640,480 size picture.
//here I'm trying to fill newly created QImage with random pixels and display it.
for(int i = 0; i < 640; i++)
{
for(int u = 0; u < 480; u++)
{
img->setPixel(i, u, rawData[i]);
}
}
ui->label->setPixmap(QPixmap::fromImage(*img));
am I doing it correctly? By the way, can you point me where should I learn these things? Thank you!
Overall it's correct. QImage is a class that allows to manipulate its own data directly, but you should use correct pixel format.
A bit more efficient example:
QImage* img = new QImage(640, 480, QImage::Format_RGB16);
for (int y = 0; y < img->height(); y++)
{
memcpy(img->scanLine(y), rawData[y], img->bytesPerLine());
}
Where rawData is a two-dimension array.
This is how I saved a raw BGRA frame to the disk:
QImage image((const unsigned char*)pixels, width, height, QImage::Format_RGB32);
image.save("out.jpg");
Syntactically, your code appears to be correct.
Reading the class signature, you may want to call setPixel in the following manner:
img->setPixel(i, u, QRbg(##FFRRGGBB));
Where ##FFRRGGBB is a color quadruplet, unless, of course, you want monochrome 8 bit support.
Additionally, declaring a naked pointer is dangerous. The following code is equivalent:
QImage image(640, 480, QImage::Format_something);
QPixmap::fromImage(image);
And will deallocate appropriately upon function completion.
Qt Examples directory is a great place to search for functionality. Also, peruse the class documentation because they're littered with examples.
Just curious if this is the best practice for initializing a dynamic, multidimensional array in D. There is a section on arrays in their language reference, but I'm not quite sure if it goes over what I'm trying to accomplish.
class Map {
Tile[][] tiles;
this(uint width, uint height) {
tiles.length = height;
foreach (ref tilerow; tiles)
tilerow.length = width;
}
}
Map map1 = new Map(5000, 3000); // values determined at runtime
(or an equivalent alternative like a typical for (y=0;y<height;y++) loop).
My concern with this is that it reallocates each row of the array separately rather than the whole chunk all at once, so I don't know if this will lead to too much memory shuffling. Also, I believe it's not guaranteed to be contiguous (since tiles is just an array of pointers in this case). Is there any "better" way to do this (that doesn't involve using a single-dimensional array and computing the index myself)? As far as I can tell from the docs a contiguous multidimensional array can only be declared with immutable dimensions at compile time, just wondering if I'm missing something...
You can new the array, at least in D2:
Tile[][] tiles = new Tile[][](height, width);
I believe this is the best practice.
you can fudge it by mallocing every thing you need upfront
this(uint width, uint height) {
void* p = enforce(GC.malloc(Tile.sizeof*width*height),new OutOfMemoryException);
//allocate all rows at once, throw on returned null
tiles.length = height;
foreach (i,ref tilerow; tiles)
tilerow = cast(Tile[])p[Tile.sizeof*width*i..Tile.sizeof*width*(i+1)];
//slice it into the multidimensional array
}
EDIT or use a temporary array to keep hem in for cleaner/less bugprone code (i.e. hide the malloc)
this(uint width, uint height) {
Tile[] p = new Tile[height*width]
tiles.length = height;
foreach (i,ref tilerow; tiles)
tilerow = p[width*i..width*(i+1)];
//slice it into the multidimensional array
}
I am using OpenMap and have to load very large dimensioned images.
I tried to load these images as big raster which fails with an OufOfMemoryException. In debug mode the layer constructor tells me that the image dimensions are too large.
In an OpenMap mailing list I found the MyJAIPlugin, which allows me to load and display GeoTiff files.
How can I show a 300mb GeoTiff in OpenMap?
I had a nearly same situation by loading hd maps with at least 690mb filesize.
I also used the JAIPlugIn from the mailing list and internaly they use the OMScalingRaster witch works with a BufferedImage. These limits your image size and causes the debug message.
I've solved it by modifieing the OMScalingRaster. I've changed the BufferedImage to a TiledImage to handle large images and fixed the upcoming errors. Here it's important you change the scaleTo(Projection thisProj)-method, to scale with JAI.
Now i can load the file and it's rendered on the map. But if you are zooming out too much, it will throw a OutOfMemoryException because in my modification i make a subimage of the part of the image that will be visible and give it as BufferedImage to the OMRaster.
Here is the mod. at the end of the scaleTo-method:
// Now we can grab the bit we want out of the source
// and
// scale it to fit the intersection.
// Calc width adjustment
float widthAdj = (float) ((double) iRect.width
/ (double) clipRect.width);
// Calc height adjustment
float heightAdj = (float) ((double) iRect.height
/ (double) clipRect.height);
// Create the transform
// JAI-Version
ParameterBlock pb = new ParameterBlock();
pb.addSource(sourceImage.getSubImage(clipRect.x,
clipRect.y,
clipRect.width,
clipRect.height).getAsBufferedImage());
pb.add(widthAdj); // The xScale
pb.add(heightAdj); // The yScale
pb.add(0.0F); // The x translation
pb.add(0.0F); // The y translation
RenderedOp newImage = JAI.create("scale",pb, null);
bitmap = newImage.getAsBufferedImage();
point1.setLocation(iRect.x, iRect.y);
// setVisible(currentVisibility);
}
} else {
bitmap = null;
}
}
For the other errors by replacing BufferedImage with TiledImage use the equivalent TiledImage-methods. But to save memory you should use the TiledImage-constructor with the sharedDataBuffer flag = true.
For Exsample this mod. can handle maps (compressed 690mb) with a scaling of 1:50000 and i can zoom out to 1:600000 before the layer says it run out of memory.
I am using a GDI+ Graphic to draw a 4000*3000 image to screen, but it is really slow. It takes about 300ms. I wish it just occupy less than 10ms.
Bitmap *bitmap = Bitmap::FromFile("XXXX",...);
//--------------------------------------------
// this part takes about 300ms, terrible!
int width = bitmap->GetWidth();
int height = bitmap->GetHeight();
DrawImage(bitmap,0,0,width,height);
//------------------------------------------
I cannot use CachedBitmap, because I want to edit the bitmap later.
How can I improve it? Or is any thing wrong?
This native GDI function also draws the image into the screen, and it just take 1 ms:
SetStretchBltMode(hDC, COLORONCOLOR);
StretchDIBits(hDC, rcDest.left, rcDest.top,
rcDest.right-rcDest.left, rcDest.bottom-rcDest.top,
0, 0, width, height,
BYTE* dib, dibinfo, DIB_RGB_COLORS, SRCCOPY);
//--------------------------------------------------------------
If I want to use StretchDIBits, I need to pass BITMAPINFO, But how can I get BITMAPINFO from a Gdi+ Bitmap Object? I did the experiment by FreeImage lib, I call StretchDIBits using FreeImageplus object, it draw really fast. But now I need to draw Bitmap, and write some algorithm on Bitmap's bits array, how can I get BITMAPINFO if I have an Bitmap object? It's really annoying -___________-|
If you're using GDI+, the TextureBrush class is what you need for rendering images fast. I've written a couple of 2d games with it, getting around 30 FPS or so.
I've never written .NET code in C++, so here's a C#-ish example:
Bitmap bmp = new Bitmap(...)
TextureBrush myBrush = new TextureBrush(bmp)
private void Paint(object sender, PaintEventArgs e):
{
//Don't draw the bitmap directly.
//Only draw TextureBrush inside the Paint event.
e.Graphics.FillRectangle(myBrush, ...)
}
You have a screen of 4000 x 3000 resolution? Wow!
If not, you should draw only the visible part of the image, it would be much faster...
[EDIT after first comment] My remark is indeed a bit stupid, I suppose DrawImage will mask/skip unneeded pixels.
After your edit (showing StretchDIBits), I guess a possible source of speed difference might come from the fact that StretchDIBits is hardware accelerated ("If the driver cannot support the JPEG or PNG file image" is a hint...) while DrawImage might be (I have no proof for that!) coded in C, relying on CPU power instead of GPU's one...
If I recall correctly, DIB images are fast (despite being "device independent"). See High Speed Win32 Animation: "use CreateDIBSection to do high speed animation". OK, it applies to DIB vs. GDI, in old Windows version (1996!) but I think it is still true.
[EDIT] Maybe Bitmap::GetHBITMAP function might help you to use StretchDIBits (not tested...).
Just a thought; instead of retrieving the width and height of the image before drawing, why not cache these values when you load the image?
Explore the impact of explicitly setting the interpolation mode to NearestNeighbor (where, in your example, it looks like interpolation is not actually needed! But 300ms is the kind of cost of doing high-quality interpolation when no interpolation is needed, so its worth a try)
Another thing to explore is changing the colour depth of the bitmap.
Unfortunately when I had a similar problem, I found that GDI+ is known to be much slower than GDI and not generally hardware accelerated, but now Microsoft have moved on to WPF they will not come back to improve GDI+!
All the graphics card manufacturers have moved onto 3D performance and don't seem interested in 2D acceleration, and there's no clear source of information on which functions are or can be hardware accelerated or not. Very frustrating because having written an app in .NET using GDI+, I am not happy to change to a completely different technology to speed it up to reasonable levels.
i don't think they'll make much of a different, but since you're not actually needing to resize the image, try using the overload of DrawImage that doesn't (attempt) to resize:
DrawImage(bitmap,0,0);
Like i said, i doubt it will make any difference, because i'm sure that DrawImage checks the Width and Height of the bitmap, and if there's no resizing needed, just calls this overload. (i would hope it doesn't bother going through all 12 million pixels performing no actual work).
Update: My ponderings are wrong. i had since found out, but guys comment reminded me of my old answer: you want to specify the destination size; even though it matches the source size:
DrawImage(bitmap, 0, 0, bitmap.GetWidth, bitmap.GetHeight);
The reason is because of dpi differences between the dpi of bitmap and the dpi of the destination. GDI+ will perform scaling to get the image to come out the right "size" (i.e. in inches)
What i've learned on my own since last October is that you really want to draw a "cached" version of your bitmap. There is a CachedBitmap class in GDI+. There are some tricks to using it. But in there end i have a function bit of (Delphi) code that does it.
The caveat is that the CachedBitmap can become invalid - meaning it can't be used to draw. This happens if the user changes resolutions or color depths (e.g. Remote Desktop). In that case the DrawImage will fail, and you have to re-created the CachedBitmap:
class procedure TGDIPlusHelper.DrawCachedBitmap(image: TGPImage;
var cachedBitmap: TGPCachedBitmap;
Graphics: TGPGraphics; x, y: Integer; width, height: Integer);
var
b: TGPBitmap;
begin
if (image = nil) then
begin
//i've chosen to not throw exceptions during paint code - it gets very nasty
Exit;
end;
if (graphics = nil) then
begin
//i've chosen to not throw exceptions during paint code - it gets very nasty
Exit;
end;
//Check if we have to invalidate the cached image because of size mismatch
//i.e. if the user has "zoomed" the UI
if (CachedBitmap <> nil) then
begin
if (CachedBitmap.BitmapWidth <> width) or (CachedBitmap.BitmapHeight <> height) then
FreeAndNil(CachedBitmap); //nil'ing it will force it to be re-created down below
end;
//Check if we need to create the "cached" version of the bitmap
if CachedBitmap = nil then
begin
b := TGDIPlusHelper.ResizeImage(image, width, height);
try
CachedBitmap := TGPCachedBitmap.Create(b, graphics);
finally
b.Free;
end;
end;
if (graphics.DrawCachedBitmap(cachedBitmap, x, y) <> Ok) then
begin
//The calls to DrawCachedBitmap failed
//The API is telling us we have to recreate the cached bitmap
FreeAndNil(cachedBitmap);
b := TGDIPlusHelper.ResizeImage(image, width, height);
try
CachedBitmap := TGPCachedBitmap.Create(b, graphics);
finally
b.Free;
end;
graphics.DrawCachedBitmap(cachedBitmap, x, y);
end;
end;
The cachedBitmap is passed in by reference. The first call to DrawCachedBitmap it cached version will be created. You then pass it in subsequent calls, e.g.:
Image imgPrintInvoice = new Image.FromFile("printer.png");
CachedBitmap imgPrintInvoiceCached = null;
...
int glyphSize = 16 * (GetCurrentDpi() / 96);
DrawCachedBitmap(imgPrintInvoice , ref imgPrintInvoiceCached , graphics,
0, 0, glyphSize, glyphSize);
i use the routine to draw glyphs on buttons, taking into account the current DPI. The same could have been used by the Internet Explorer team to draw images when the user is running high dpi (ie is very slow drawing zoomed images, because they use GDI+).
/*
First sorry for ma English, and the code is partly in polish, but it's simple to understand.
I had the same problem and I found the best solution. Here it is.
Dont use: Graphics graphics(hdc); graphics.DrawImage(gpBitmap, 0, 0); It is slow.
Use: GetHBITMAP(Gdiplus::Color(), &g_hBitmap) for HBITMAP and draw using my function ShowBitmapStretch().
You can resize it and it is much faster! Artur Czekalski / Poland
*/
//--------Global-----------
Bitmap *g_pGDIBitmap; //for loading picture
int gRozXOkna, gRozYOkna; //size of working window
int gRozXObrazu, gRozYObrazu; //Size of picture X,Y
HBITMAP g_hBitmap = NULL; //for displaying on window
//------------------------------------------------------------------------------
int ShowBitmapStretch(HDC hdc, HBITMAP hBmp, int RozX, int RozY, int RozXSkal, int RozYSkal, int PozX, int PozY)
{
if (hBmp == NULL) return -1;
HDC hdc_mem = CreateCompatibleDC(hdc); //utworzenie kontekstu pamięciowego
if (NULL == hdc_mem) return -2;
//Trzeba połączyć BMP z hdc_mem, tzn. umieścić bitmapę w naszym kontekście pamięciowym
if (DeleteObject(SelectObject(hdc_mem, hBmp)) == NULL) return -3;
SetStretchBltMode(hdc, COLORONCOLOR); //important! for smoothness
if (StretchBlt(hdc, PozX, PozY, RozXSkal, RozYSkal, hdc_mem, 0, 0, RozX, RozY, SRCCOPY) == 0) return -4;
if (DeleteDC(hdc_mem) == 0) return -5;
return 0; //OK
}
//---------------------------------------------------------------------------
void ClearBitmaps(void)
{
if (g_hBitmap) { DeleteObject(g_hBitmap); g_hBitmap = NULL; }
if (g_pGDIBitmap) { delete g_pGDIBitmap; g_pGDIBitmap = NULL; }
}
//---------------------------------------------------------------------------
void MyOpenFile(HWND hWnd, szFileName)
{
ClearBitmaps(); //Important!
g_pGDIBitmap = new Bitmap(szFileName); //load a picture from file
if (g_pGDIBitmap == 0) return;
//---Checking if picture was loaded
gRozXObrazu = g_pGDIBitmap->GetWidth();
gRozYObrazu = g_pGDIBitmap->GetHeight();
if (gRozXObrazu == 0 || gRozYObrazu == 0) return;
//---Uworzenie bitmapy do wyświatlaia; DO IT ONCE HERE!
g_pGDIBitmap->GetHBITMAP(Gdiplus::Color(), &g_hBitmap); //creates a GDI bitmap from this Bitmap object
if (g_hBitmap == 0) return;
//---We need to force the window to redraw itself
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}
//---------------------------------------------------------------------------
void MyOnPaint(HDC hdc, HWND hWnd) //in case WM_PAINT; DO IT MANY TIMES
{
if (g_hBitmap)
{
double SkalaX = 1.0, SkalaY = 1.0; //scale
if (gRozXObrazu > gRozXOkna || gRozYObrazu > gRozYOkna || //too big picture, więc zmniejsz;
(gRozXObrazu < gRozXOkna && gRozYObrazu < gRozYOkna)) //too small picture, można powiększyć
{
SkalaX = (double)gRozXOkna / (double)gRozXObrazu; //np. 0.7 dla zmniejszania; FOR DECREASE
SkalaY = (double)gRozYOkna / (double)gRozYObrazu; //np. 1.7 dla powiększania; FOR INCREASE
if (SkalaY < SkalaX) SkalaX = SkalaY; //ZAWSZE wybierz większe skalowanie, czyli mniejszą wartość i utaw w SkalaX
}
if (ShowBitmapStretch(hdc, g_hBitmap, gRozXObrazu, gRozYObrazu, (int)(gRozXObrazu*SkalaX), (int)(gRozYObrazu*SkalaX), 0, 0, msg) < 0) return;
Try using copy of Bitmap from file. FromFile function on some files returns "slow" image, but its copy will draw faster.
Bitmap *bitmap = Bitmap::FromFile("XXXX",...);
Bitmap *bitmap2 = new Bitmap(bitmap); // make copy
DrawImage(bitmap2,0,0,width,height);
I have made some researching and wasn't able to find a way to render images with GDI/GDI+ more faster than
Graphics.DrawImage/DrawImageUnscaled
and at the same time simple like it.
Till I discovered
ImageList.Draw(GFX,Point,Index)
and yeah it's really so fast and simple.