I am creating an app in Qt, similar to scribble (given in sample app). Purpose is to let user draw freehand and once finished, upload these drawing to net. As of now I am saving the drawing as PNG image every 5 seconds, so that the loss of data in an event of unexpected shut-down is minimum (I kept 5 sec to minimize write operations; Real real-time would be highly desirable).
But the problem is, I am saving the entire page as an image every 5 seconds, where the new data added may be few pixels. I was wondering if I could write ONLY the new pixels added into the disk; there is no constraint that I should use PNG while saving; I can convert the data to PNG at the end when user says he is finished.
The piece of code for saving very basic;
void SaveData(const QString &fileName, const char *fileFormat, QImage image)
{
mutex.lock();
QImage visibleImage = image;
if (visibleImage.save(fileName, fileFormat, 50))
{
system("sync");
mutex.unlock();
return true;
} else {
mutex.unlock();
return false;
}
}
I just wonder if REAL real-time save as the pixels getting added if possible..!
Thanks in advance
DK
I suggest you to use tiles to save the image. Split the canvas to many e.g. 64x64 rectangles. And save each rectangle into separate file. When something is changed, you need to rewrite only few small files instead of rewriting the whole picture.
Also there is another dangerous thing in your code. When you run QImage::save, it most likely will erase file contents and write new contents. If the system was shutted down between there two actions, your file will became empty. So it's important to write new contents to a temporary file and then move it to the proper location. Keeping several old version of a file also can be useful. Who knows how the file system will react on the shutdown.
You could maybe use a memory mapped file, something like:
QFile file("rawimage.dat");
file.open(QIODevice::ReadWrite);
// Make sure there is enough memory for the image
quint32 width = 16;
quint32 height = 16;
quint32 bpp32 = 4;
qint64 file_size = width * height * bpp32;
file.resize(file_size);
uchar* mem = file.map(0, file_size);
// make a QImage that uses the file as memory
QImage img(mem, 16, 16, QImage::Format_ARGB32);
// Do some drawing in the image
img.fill(0);
// finished with the file
file.unmap(mem);
file.close();
You will need to check that it actually flushes to disk correctly - I haven't tested this. Ideally on Windows you'd want to be able to call 'FlushViewOfFile' on the memory mapped handle to ensure that modified pages are written to disk. It doesn't look like there is a way of calling this in Qt so you might need to do something operating system specific here to ensure that the disk image is consistent when you want it to be.
You could create a list of QPainterPath objects of the drawn items, which are then rendered to the QImage. You'd need to change the mouse events to do the following: -
Mouse Down : create a new QPainterPath (painterPath) and call painterPath->moveTo
Mouse Move : call painterPath->LineTo
Mouse Up : Store the QPainterPath in a list.
In the paint event, you then pass each new QPainterPath to be drawn
To back up, every n seconds, open up a file and append a stream of the new QPainterPaths since the last time the list was saved.
To restore, open the file, stream them back in and draw them on to the Image.
This can be optimised to check for new items and not to bother saving if none exist. In addition, rather than being time based, you could maintain a number of points that are created in the QPainterPath and only save when it exceeds a certain number.
Note that if you do go down this route, you may also want to store Painter settings with each QPainterPath, if the user can also change things such as pen colour, width etc.
Other advantages come with using QPainterpath - for example, the user could open up one image and then a second, choosing to have it drawn on top of the first.
If you want real-time saving then I suggest you use an uncompressed bitmap format. Changing pixels would be as simple as seeking inside the file to the x-y co-ordinates, usually calculated as
file.seek(y * lineWidth + x * pixelDataSize);
file.write(pixelData);
Related
I'm developing a painter-like application and i want my application to have multiple layers for different drawings. For that purpose i have an array, that contains QPixmaps with transparent background and i have a function that merges two QPixmaps (draws one on another). It's done like so:
void MeasuresWidget::MergePixmaps(QPixmap source, QPixmap target)//draw source on target
{
QPainter painter(&target);
painter.drawPixmap(target.rect(),source,source.rect());
painter.end();
imageLabel->setPixmap(target);
}
I'm for 100% sure that array of pixmaps (AllLayers array) contains all the drawing i want. Now i want to consistently merge all the drawings with the original image. Here's how i'm trying to achieve this:
void MeasuresWidget::on_actionAct_triggered()
{
ForMerging = &OriginalImage;
for(int i=0;i<5;i++)
MergePixmaps(AllLayers[i], *ForMerging);
}
where ForMerging is temporary QPixmap object for, well, merging, and OriginalImage is, undoubtedly, QPixMap, that contains original image.
Again, i'm 100% sure, that all layers contain it's image on transparent background. The problem i'm facing is that in result original image is merged only with the last drawing, i.e. with AllLayers[4]. If i make i to run from 0 to 2(not including), for example, the result will be original image merged only with AllLayers[1]. I've struggled with this problem for a period of time and have no idea what might be wrong so i'm seeking for any help possible.
Try merge all QPixmap at the same call to MergePixmaps. For this change your Source variable at MergePixmap function to your AllLayers object like this:
void MeasuresWidget::MergePixmaps(AllLayers *source, QPixmap target)//draw source on target
{
QPainter painter(&target);
for(int i = 0; i < source->lenght();i++){
painter.drawPixmap(target.rect(),source->at(i),source->at(i).rect());
}
painter.end();
imageLabel->setPixmap(target);
}
I have very a simply application written in QT in which I want to display a movie by using QMediaPlayer, but before I will display any frame I would like to detect on it some objects and mark them by drawing a rectangle over it.
I've read in http://doc.qt.io/qt-5/videooverview.html that I can access each frame by subclassing QAbstractVideoSurface and so I dit it.
class VideoSurface : public QAbstractVideoSurface {
Q_OBJECT
bool present(const QVideoFrame &frame) override {
if (surfaceFormat().pixelFormat() != frame.pixelFormat()
|| surfaceFormat().frameSize() != frame.size()) {
setError(IncorrectFormatError);
stop();
return false;
} else {
currentFrame = frame;
return true;
}
}
...
}
Now, I am receiving in this member function frames that I want to modify by drawing on it rectangles in places where I detected objects and then I would like to display them on the screen (preferably on some widget).
How can I do this?
Should my VideoSurface class contain QWidget as a member? or should I subclass QWidget which will contain VideoSurface?
In both cases, how can I display this frame? Should I first convert it to QImage and then display (it would be convinien for me, because my detection system is working with QImage, but would it be efficient)? I know that I can't paint from outside a paint event, so I can't paint in present function, so where exactly should be this painting function and how I can call it?
Where should I detect those object and modify frame? In present function, or in drawing function?
This is up to you and depends on how you prefer to structure your classes. I would prefer to have a separate widget that holds a pointer to your VideoSurface and draws the data that is returned by some member function of VideoSurface (depends on your solution in 2.)
a) QImage is efficient enough for some purposes and if you are already using it in your detection code, then you already have everything in memory and can work on that. As with all performance-related worries: Test and see if the performance is good enough for you. If it is not, you probably also have to consider doing the detection in a different way. I have worked on a project where we processed QImages converted from a similar VideoSurface on a camera stream on mobile devices (for relatively low-resolution images), and the performance was good enough that we haven't yet bothered to use other techniques. The source code for the VideoSurface class in that project (Neuronify) is hosted here.
b) Your present() function could emit a signal that you can connect to from other objects that fetch the latest data from the VideoSurface and keeps it until their paint-function is called. Or you could apply the data directly to some widget that accepts image data. See Use of QAbstractVideoSurface for an example of that.
Again, that is up to you :) However, if you need to improve performance at some point you might want to do that work on a different thread to keep your GUI from locking up while you are processing the data. And if you do that, you need to decide whether you have to process every frame or if some frames can skip processing to improve the FPS of the playback. In the latter case, you should probably not do it in the present() function, as that is likely going to keep the media player from being able to feed you more frames while you are processing old frames.
I am currently using the 16-bit libnds (Whith devkitpro) example as a basis and am trying to display text and the png background image on the same screen (in this example it is the top sceen). I am having a similar issue as this post.
I have garbage on the top of the screen (only ifconsoleInit(...) is called), similar to the first problem in the thread. The only problem is that I am displaying the background image in a different method so the fixes they made in that thread did not apply to this.
All I am looking for is whether there is a way to fix the garbage on the top of the screen. If there is a more efficient/better way to display the image, I am willing to accept it, just I haven't found a detailed enough tutorial on how to load an image as a background without using this method. Any help would be appreciated. I will answer any further questions anyone has about what is not working.
You can find the project attached here.
Sorry for the long delay but there are a few issues with your code. The first is that in Mode 4 the only background that can be set up as a 16 bit bitmap is layer 3. http://answers.drunkencoders.com/what-graphics-modes-does-the-ds-support/
Next, the layers all share a single chunk of background memory and your garbage is coming from you overwriting part of the bitmap in video memory with the characters for the font and the map for the console background. A simple solution is to move the bitmap by settings its map base to 1. This offsets its in graphics memory by 16KB which leaves 16KB of room for your text layer (this only works because we cant display the entire 256x256 image on screen at once due the the resolution of the DS as 256x256x2bytes fills up all of memory bank A...to be more correct we should assign another memory bank to the main background...but since we cant see the bottom 70 or so lines of pixels of our image anyway its okay that they didnt quite make it into video memory).
libnds also has a macro to make finding the memory for your background a bit simpler called "bgGetGfxPtr(id)" which will get a pointer to your background gfx in video memory after you set it up so you dont have to try to calculate it via an offset from BG_GFX.
In all the changes to your code should look like this (I added a version of this to the libnds code faq at : http://answers.drunkencoders.com/wp-admin/post.php?post=289&action=edit&message=1)
int main(void) {
//Top screen pic init
videoSetMode(MODE_4_2D);
vramSetBankA(VRAM_A_MAIN_BG);
int bg = bgInit(3, BgType_Bmp16, BgSize_B16_256x256, 1,0);
decompress(drunkenlogoBitmap, bgGetGfxPtr(bg), LZ77Vram); //Displays/decompresses top image
//videoSetMode(MODE_4_2D);
consoleInit(0,0, BgType_Text4bpp, BgSize_T_256x256, 4,0, true, true);
iprintf("\x1b[1;1HThe garbage is up here ^^^^^.");
iprintf("\x1b[21;1HTesting the text function...");
while(1) {
swiWaitForVBlank();
scanKeys();
if (keysDown()&KEY_START) break;
}
return 0;
}
So, this is my problem: I have this very big image, and I want to show only a specific part of it. After the user pressing a specific key I want the image to move, showing another part of it. The transition from one part of the image to another have to be smooth, animated.
I tried using a QLabel to show the image but it always shows the center of the image, and I do not really know how to make the animation. What would you guys suggest?
Interesting question. Here is something I just tested and seems to work.
Add a QGraphicsView with dimensions the dimensions of the part of the image you want to display, eg 100x100. Create a QGraphicsScene and add it to the view:
QGraphicsScene* pScene = new QGraphicsScene(this);
ui->graphicsView->setScene(pScene);
Now add your image into the scene. In my case I has an image in my resource file. The trick is to set the sceneRect to the position you want to display. I wanted to display an 100x100 part of the image starting from 0,300 :
pItem = pScene->addPixmap(QPixmap::fromImage(QImage(":/photos/image")));
pScene->setSceneRect(0,300,100,100);
In order to test the smooth moving I added a button which when clicked is triggering a slot called move. This slot simply updates the sceneRect. In my simple example I just move the image 100 pixels right. In a real world scenario you could also move it diagonally or vertically and check the image limits.
void move()
{
for (unsigned i=currentX; i<currentX + 100; i++)
{
ui->graphicsView->scene()->setSceneRect(i,300,100,100);
qApp->processEvents();
}
currentX += 100;
}
Notice the currentX variable. It is nothing more than the last image position. Also we must call the processEvents in order to "see" the image moving smoothly.
You could use QPixmap::copy( int x, int y, int width, int height ) to copy a region of the image and display that.
Few options:
Try using a Q3CanvasSprite (within in a Q3Canvas). It is designed more for splitting one image into multiple ones but not for animating between them. You could try abusing it and declaring (say) 100 frames (10 per digit, which would be used as animation steps) or just use the move() method.
Try QGraphicsPixmapItem::setOffset() (within a QGraphicsScene). This might be overkill as QGraphicsScene is made for large number of images).
I'm not sure, but maybe this can be done with QStateMachine and QAbstractAnimation.
I'm creating a image visualizer that open large images(2gb+) in Qt.
I'm doing this by breaking the large image into several tiles of 512X512. I then load a QGraphicsScene of the original image size and use addPixmap to add each tile onto the QGraphic Scene. So ultimately it looks like a huge image to the end user when in fact it is a continuous array of smaller images stuck together on the scene.First of is this a good approach?
Trying to load all the tiles onto the scene takes up a lot of memory. So I'm thinking of only loading the tiles that are visible in the view. I've already managed to subclass QGraphicsScene and override its drag event thus enabling me to know which tiles need to be loaded next based on movement. My problem is tracking movement on the scrollbars. Is there any way I can create an event that get called every time the scrollbar moves. Subclassing QGraphicsView in not an option.
QGraphicsScene is smart enough not to render what isn't visible, so here's what you need to do:
Instead of loading and adding pixmaps, add classes that wrap the pixmap, and only load it when they are first rendered. (Computer scientists like to call this a "proxy pattern"). You could then unload the pixmap based on a timer. (They would be transparently re-loaded if unloaded too soon.) You could even notify this proxy path of the current zoom level, so that it loads lower resolution images when they will be rendered smaller.
Edit: here's some code to get you started. Note that everything that QGraphicsScene draws is a QGraphicsItem, (if you call ::addPixmap, it's converted to a ...GraphicsItem behind the scenes), so that's what you want to subclass:
(I haven't even compiled this, so "caveat lector", but it's doing the right thing ;)
class MyPixmap: public QGraphicsItem{
public:
// make sure to set `item` to nullptr in the constructor
MyPixmap()
: QGraphicsItem(...), item(nullptr){
}
// you will need to add a destructor
// (and probably a copy constructor and assignment operator)
QRectF boundingRect() const{
// return the size
return QRectF( ... );
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget){
if(nullptr == item){
// load item:
item = new QGraphicsPixmapItem( ... );
}
item->paint(painter, option, widget);
}
private:
// you'll probably want to store information about where you're
// going to load the pixmap from, too
QGraphicsPixmapItem *item;
};
then you can add your pixmaps to the QGraphicsScene using QGraphicsScene::addItem(...)
Although an answer has already been chosen, I'd like to express my opinion.
I don't like the selected answer, especially because of that usage of timers. A timer to unload the pixmaps? Say that the user actually wants to take a good look at the image, and after a couple of seconds - bam, the image is unloaded, he will have to do something in order the image to reappear. Or may be you will put another timer, that loads the pixmaps after another couple of seconds? Or you will check among your thousand of items if they are visible? Not only is this very very irritating and wrong, but that means that your program will be using resources all the time. Say the user minimizes you program and plays a movie, he will wonder why on earth my movie is freezing every couple of seconds...
Well, if I misunderstood the proposed idea of using timers, execuse me.
Actually the idea that mmutz suggested is better. It reminded me of the Mandelbrot example. Take a look at it. Instead of calculating what to draw you can rewrite this part to loading that part of the image that you need to show.
In conclusion I will propose another solution using QGraphicsView in a much simpler way:
1) check the size of the image without loading the image (use QImageReader)
2) make your scene's size equal to that of the image
3) instead of using pixmap items reimplement the DrawBackground() function. One of the parameters will give you the new exposed rectangle - meaning that if the user scrolls just a little bit, you will load and draw only this new part(to load only part of an image use setClipRect() and then read() methods of the QImageReader class). If there are some transformations you can get them from the other parameter(which is QPainter) and apply them to the image before you draw it.
In my opinion the best solution will be to combine my solution with the threading shown in the Mandelbrot example.
The only problem that I can think of now is if the user zooms out with a big scale factor. Then you will need a lot of resources for some time to load and scale a huge image. Well I see now that there is some function of the QImageReader that I haven't tried yet - setScaledSize(), which maybe do just what we need - if you set a scale size and then load the image maybe it won't load first the entire image – try it. Another way is just to limit the scale factor, a thing that you should do anyway if you stick to the method with the pixmap items.
Hope this helps.
Unless you absolutely need the view to be a QGraphicsView (e.g. because you place other objects on top of the large background pixmap), I'd really recommend just subclassing QAbstractScrollArea and reimplementing scrollContentsBy() and paintEvent().
Add in a LRU cache of pixmaps (see QPixmapCache for inspiration, though that one is global), and make the paintEvent() pull used pixmaps to the front, and be set.
If this sounds like more work than the QGraphicsItem, believe me, it's not :)