A question on using QSharedPointer with QImages
-
I'm dealing with a large image and am doing several different processes on the QImage. Based on my research, I believe QSharedPointer is the correct answer. Is this correct? Because the image is so large, I don't want do copies of it and I want it to stay alive until all the processes are done. However when it is finished and is no longer needed, I want to release the memory. While trying to implement it, it crashes between processes...this leads me to believe the pointer is getting deleted before I want it to. Are there any good examples on using QSharedPointer or are there better ways of doing the same thing?
I'm probably getting wrapped around the axle by misreading/misinterpreting the documentation.
@
void mainImgFunction()
{
QSharedPointer<QImage> img(new QImage("c:/bigImg.png"));
*img = process1(*img);
*img = process2(*img);//it crashes here}
QImage process1(QImage img)
{
/do the processing/
return img;
}QImage process2(QImage img)
{
/do the processing/
return img;
}@
-
I don't think QSharedPointer is what you need. In general, QImage is implicitly shared - as long as there is only one existing QImage instance referencing the image data, the data will never be copied. Additionally the internal use of copy-on-write ensures that copies are only created when more than one QImage object references the same data and one of the instances performs an operation that (potentially) modifies the image.
Implicit sharing is used by numerous Qt classes - you rarely need to worry about unwanted copies, especially if you generally pass objects around by (const) reference as recommended.
-
Arnold,
Thank you for your response. The one problem when using just QImage, the memory seems like it is never released....that is one of the reason for looking at smart pointers. I've tried manually managing the QImage memory, but that usually ends with a crash. The only way I found to get QImage to release is to create it on the stack. However, since this image is so large, I want to put it on the heap. I tried to use QScopedPointer, but It seemed to have the same problem....this is when I started delving into the documentation deeper. QScopedPointer works wonderfully, except when I have to spread things across different functions.Cheers
-
I seriously doubt that the buffer holding the actual image data is stack-allocated by QImage, AFAIK it is not even possible since the buffer size required for a random image loaded from a file is not known at compile time. So even if you stack-allocate the QImage object, the actual image data will be located in heap memory.
-
Arnold,
I've tried the following code and could never get it to work the way I wanted to. I'm probably doing something wrong, but once the image is instantiated, the memory increase to the size of the image but never decreases when it goes out of scope. I've tried delete img and img->deleteLater() but both ways results in a crash. I was hoping I could get smart pointers to work across multiple functions. It sounds like you have pretty in-depth knowledge of the inner workings of QImage.....can you point me in the right direction?Thanks
@
void testQimage()
{
mainImgFunction();
// memory never decreases once img is instantiated
// however if img is created on the stack, memory decrease back to what
// it was before the function is called
}void mainImgFunction()
{
QImage *img = new QImage("c:/bigImg.png");
// memory increase by the size of the image
img = process1(img);
img = process2(img);
//write image to file
//delete img or img->deleteLater() causes crash}
QImage process1(QImage img)
{
/do the processing/
return img;
}QImage process2(QImage img)
{
/do the processing/
return img;
}
@ -
As I said it is perfectly ok to just use
@QImage image("c:/bigImg.png");@
instead of heap allocation since the large image data is allocated on the heap anyway. That aside, looking at your code I don't see any reason why this should crash. I suspect that you do have an error in the processing code you omitted. I assume that process1 and process2 are called sequentially in the same thread that creates the image - at least the code sample suggests that. So the processing itself is the most likely cause for your problem.
-
Arnold,
Thanks for your help. It seems counter intuitive that creating an object on the stack would allocate memory on the heap.I'm going to follow your advice and use QImage image("c:/bigImg.png");. However, it is bugging me on the correct way to implement this using smart pointers. How would you use a smart pointer across multiple function calls?
Cheers
-
As I say the code you posted does not reveal any obvious errors (it wouldn't compile though due to function parameter mismatches). If i really need to allocate the QImage on the heap the following should work:
@
void process1(QImage* img) {
// process ...
}void process2(QImage* img) {
// process ...
}void mainFunc() {
QScopedPointer<QImage> image = new QImage("c:/bigImg.png");process1(image);
process2(image);
}
@I still suspect that your processing code has issues so I suggest you double-check that. Even if the problem does not occur using a stack-allocated QImage your code might cause corruption of the heap memory which lead to arbitrary errors that are very hard to spot.
As for a stack-allocated class allocating memory on the heap - consider the following very simplified image class:
@
class Image {private:
uchar* data;public:
Image(const QString& imagepath) {
int buffersize = useMagicToGetImageBufferSize(imagepath);
data = new uchar[buffersize];
loadImageData(imagepath, buffer);
}~Image() {
delete data;
}}:
@Since the actual size required for the image cannot be known beforehand the image class has to determine the required size and allocate a suitable buffer on the heap. Not that counter-intuitive if you think about it and quite common :)