(Qt C++) Resize pixmap and KEEP pixelation? - c++

In my project I have a QLabel that I change the pixmap frequently like this:
ui->frameLabel->setPixmap(slot_pic[blockId[currentSlot]][damageId[currentSlot]]);
slot_pic is simply a 2d map. So you can look at it clearer like this:
ui->frameLabel->setPixmap(pixmap);
The image is 16x16 in size and my label is 32x32. I have scaledContents checked so when the pixmap changes, the image is double in size. However, the image is now blurry. I understand why, but I was wondering if there is a way to make it stay pixelated. I want to just have a bigger pixelated image. (The image is from Minecraft if that helps you understand what I mean)
Thanks for your time :)

Don't let the QLabel do the scaling. Instead, do the scaling by yourself using QPixmap::scaled(). Something like this:
ui->frameLabel->setPixmap(
pixmap.scaled(32, 32, Qt::IgnoreAspectRatio, Qt::FastTransformation));
The important parameter is the last one, transformMode, which tells whether bilinear filtering is used or not.

Related

Qgraphicview resizing when windows resizing

I would like to ask you about an issue I do not succeed to fix.
I have a QApplication which load an image in the first mainwindow.
The code is the following:
QGraphicsScene *scene = new QGraphicsScene;
QPixmap pixmap(QString::fromStdString("image.png");
scene->addPixmap(pixmap);
ui->graphview->setScene(scene);
ui->graphview->show();
I am trying to fit the image to scale in the QGraphivsView, even when I resized my windows.
However, the image is displayed with its own size at running time and this size is not changing when windows is resized. For example, increasing my windows does not increase the image size and similarly for decreasing.
I tried even by addind the following code:
ui->graphview->fitInView(pixmap, Qt::KeepAspectRatioByExpanding);
But nothing is working.
I provide you an example on the following image of what is happening.
I have found a way however, it is not 100% satisfying.
I have replaced the resizeEvent function as on the example below:
void QMainWindows::resizeEvent(QResizeEvent *){
QRectF bounds = ui->graphQSYS->scene()->sceneRect();
ui->graphQSYS->fitInView(bounds, Qt::KeepAspectRatioByExpanding);
ui->graphQSYS->centerOn(bounds.center());
}
However, when resizing, the image quality is becoming really bad. Text written on it cannot be read anymore. Do you know another way to keep picture quality ?
Thank you very much.

Add a border to a QPixmap

I load a QPixmap from a file. I want to add a border around the QPixmap, say 4px white on each side.
What is the fastest way to do this? Do I have to create a second larger QPixmap and draw the first into the second or is there some way to extend the existing one without scaling (I've noticed that there is a QPixmap::transformed() but don't know if this is suitable in my case.
Do I have to create a second larger QPixmap and draw the first into the second
Yes.
is there some way to extend the existing one without scaling
Yes. The larger pixmap will take an unscaled copy of the original one.
The foregoing assumes that you care about the resulting pixmap. If you don't care about it, but only care to display the pixmap on a window/widget with some border, then simply draw the border and pixmap separately using QPainter, OpenGL geometry, etc.

Drawing text with shadow on pixmap with QPainter

I am working on a project in C++ with Qt, and I am trying to find a way to apply a text shadow when drawing text on a QPixmap using QPainter.
I understand that QGraphicsDropShadowEffect is a thing, and I am using in other parts of my project, but I can't for the life of me find a way to apply a QGraphicsEffect when drawing with QPainter on a pixmap. Drawing the same text multiple times with different offsets and opacities doesn't quite cut it.
Are there any ways to do this?
If not, how could I go about making a function that does it, given a QGraphicsEffect to get the radius and color from?
Thanks in advance!
I don't think it is directly possible to "draw text with shadow", it is only possible to apply a shadow to something already drawn that would take in an element and use say its alpha channel to calculate the shadow.
You should use composition, either of the final products or during drawing. It should work if you use it on a text element. The other option would be to draw your text in black, apply Gaussian blur and then again draw the text on top of it with the desired offset.
Thanks for your answer ddriver, it made me search with some new keywords, which lead me to find a suiting solution for my project.
What I figured out is that you can simply create a QLabel with the text and effects you want (QGraphicsDropShadowEffect, in my case), and render it into a QPixmap using QWidget::grab(). You can then draw this new pixmap with QPainter as you would any other image, by converting your pixmap to a QImage and using QPainter's drawImage().

How to show part of an image using QT?

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.

Maximum Size of QPixmap/QImage Windows

I have a QGraphicsView for a very wide QGraphicsScene. I need to draw the background in drawBackground() and the background is a bit complicated (long loop) although it doesn't need to be repainted constantly. I store it in a static QPixmap (I tried QImage too) inside the function drawBackground() and that pixmap is what I draw onto the painter of the view. Only when needed is the QPixmap painted on again.
If I didn't use a static pixmap, the complicated background would be generated every time I scroll sideways for example. The problem is that apparently there is a maximum width for pixmaps on Windows, on my computer it's 32770. I could store a list of pixmaps and draw them side by side but it would make the code uglier and I also don't know what the maximum width of a pixmap is for every Windows machine. Since this might be a well-known problem I was wondering if anyone has a better solution.
Thanks.
You can probably avoid the windows limit by using unaccelerated raster paint device, but 32770*1024 is 100MiB of pixmap; you probably don't want to do that even if Windows would let you.
You've already thought of the usual answer (tile it in more reasonably-sized chunks and load/generate them on demand). The other piece of the usual solution is to use something like QPixmapCache to keep the recently-used tiles so you don't regenerate them too often (only when the user scrolls a long way).
You didn't say how complex your complex background is, but you might also want to look at the Mandelbrot set example for how to do piecewise rendering of an (infinitely) large background pixmap on-demand, without blocking the UI.
This is the common use case for the tiling pattern. Basically you split the background into small images.
I'm not sure why you think "it would make the code uglier". It is certainly not a one-liner. Depending whether you have fixed size background image or not, the tiling code is usually pretty straightforward.