Smoothing a circle - painter drawing - c++

I am using the following code to draw an image in a circle.
QPixmap target(110, 130); // the size may vary
target.fill(Qt::transparent);
QPixmap p = QPixmap::fromImage( QImage(":/Images/head").scaled(110,110,Qt::IgnoreAspectRatio,Qt::SmoothTransformation).convertToFormat(QImage::Format_ARGB32));
QPainter painter(&target);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::Antialiasing, true);
QRegion r(QRect(10,10,100,100), QRegion::Ellipse);
painter.setClipRegion(r);
painter.drawPixmap(0, 0,p);
ui->label->setPixmap(target);
This is the output that I get
Notice the circle is not smooth. My question is how could i make this circle smooth.

Using clip path will not has antialias ability in drawing.
You can use a mask image or make a color mask image to draw image to a special shape that.
Here is a simple example:
QPainter *painter....
QPixmap pixmapToDraw....
....
QImage dst(sourceRect.width(), sourceRect.height(), QImage::Format_ARGB32);
dst.fill(QColor(0, 0, 0, 0));
QPainter p(&dst);
QPainterPath path;
path.addRoundedRect(0, 0, sourceRect.width(), sourceRect.height(), sourceRect.width() / 2, sourceRect.height() / 2);
p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, true);
p.fillPath(path, QBrush(Qt::white));
p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
p.drawPixmap(dst.rect(), pixmapToDraw);
painter->drawImage(0, 0, dst);
Here is Qt's doc Image Composition Example, QPainter::CompositionMode

Related

Get QImage pixel coordinates

I currently draw a QImage to a QWidget with the following code:
QPainter c(this);
const QRect& cw = c.window();
c.setRenderHint(QPainter::SmoothPixmapTransform, cw.width() < scale);
c.drawImage(cw, image);
The issue is now that the image has been properly scaled to fit in the QWidget, I would like to get the pixel coordinates on the QImage based on the QWidget coordinates. Without scaling this would be fairly trivial, as widget pixels correspond directly to QImage coordinates. Ultimately, a way to translate which pixel on the QWidget corresponds to the pixel in the QImage when scaled is ideal. Any advice is much appreciated!
What you have implemented is an escalation, to recover a coordinate we can use QTransform as I show below:
QTransform tr;
tr.scale(image.width()*1.0/cw.width(), image.height()*1.0/cw.height());
QPoint imagePoint = tr.map(widgetPoint);

Qt rotating shapes

I'm experimenting with drawing and rotating shapes in Qt, but I'm really at a loss as to how it works. Currently I have code which draws a rectangle with a small triangle on top of it. I want to rotate the shape by 35 degrees, so I try this:
void Window::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::white);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
painter.fillRect(0,0,800,800,brush);
brush.setColor(Qt::red);
painter.translate(s.getX()-5,s.getY()-8);
painter.rotate(35);
painter.fillRect(0,0,10,16,brush);
QPolygon pol;
pol.setPoints(3,0,0,10,0,5,10);
QPainterPath p;
p.addPolygon(pol);
painter.fillPath(p,brush);
}
(Ignore the s.getX/Y() calls, for now x is 150 and y is 750.)
Without the rotating and translating the code works fine and draws the shape. With the current code only the rectangle is displayed, not the polygon. How do I rotate these shapes?
You need to properly understand how affine transformations work. Without the proper understanding you will have hard times achieving what is needed.
rotate rotates everything around center of coordinates: (0,0)
translate moves center of coordinates to a new position
Your code rotates everything around the point (s.getX() - 5, s.getY() - 8).
So here's the code that will rotate both shapes 35 degrees around the center of red rectangle:
QPainter painter(this);
QBrush brush;
brush.setStyle(Qt::SolidPattern);
brush.setColor(Qt::white);
painter.setBrush(brush);
painter.setPen(Qt::NoPen);
painter.fillRect(0, 0, 800, 800, brush);
brush.setColor(Qt::red);
painter.translate(150, 750);
painter.translate(5, 8); // move center of coordinates to the center of red rectangle
painter.rotate(35); // rotate around the center of red rectangle
painter.translate(-5, -8); // move center of coordinates back to where it was
painter.fillRect(0, 0, 10, 16, brush);
QPolygon pol;
pol.setPoints(3, 0, 0, 10, 0, 5, 10);
QPainterPath p;
p.addPolygon(pol);
brush.setColor(Qt::blue);
painter.fillPath(p, brush);
Without transformations:
With transformations:

Valid PointSize for QFont

I have a QOpenGLWidget in which I draw text with a QPainter. I would like to implement a snapshot feature by rendering the widget content into a QOpenGLFrameBuffer and converting the frame buffer into a QImage.
Unfortunately, if I set the font's point size too high (> 46), the text appears as a black bar in the snapshot, while in the widget it is displayed correctly. See below an example snapshot where the block over the line is supposed to be text with font size > 46.
Here is the simplified code to render the image (it should work, because it is correctly displayed in the QOpenGLWidget):
void renderSomething(const int x, const int y, const QString & str, const int fontSize) {
// 1. draw the line
// (calculate min_x, max_x, y and z values)
glLineWidth(3);
glColor3f(0., 0., 0.);
glBegin(GL_LINES);
glVertex3f(min_x, y, z);
glVertex3f(max_x, y, z);
glEnd();
// 2. draw the text
GLint gl_viewport[4];
glGetIntegerv(GL_VIEWPORT, gl_viewport);
backup_gl_state();
QOpenGLPaintDevice paintDevice(gl_viewport[2], gl_viewport[3]);
QPainter painter(&paintDevice);
painter.setFont(QFont(painter.font().family(), fontSize);
painter.setPen(Qt::black);
painter.drawText(x, y, str);
painter.end();
restore_gl_state();
}
Here is the code for storing a snapshot:
void Viewport::takeSnapshot(const QString & path, const int size) {
glPushAttrib(GL_VIEWPORT_BIT);
glViewport(0, 0, size, size); // since size varies I want the text to scale with it
QOpenGLFramebufferObject fbo(size, size, QOpenGLFramebufferObject::Depth);
fbo.bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
renderer.renderSomething(100, 100, "Test", 50); // Here is the render call!
QImage fboImage(fbo.toImage());
QImage image(fboImage.constBits(), fboImage.width(), fboImage.height(), QImage::Format_RGB32);
image.save(path);
glPopAttrib();
fbo.bindDefault();
fbo.release();
}
EDIT
I found a workaround by using QPainter to directly draw the text on the QImage instead of drawing into the FBO. No black bar anymore, but it complicates my code and I would be happy to find a real solution...
The solution was simple: Re-read the documentation on QOpenGLFrameBufferObject in which it says:
Create the QOpenGLFrameBufferObject instance with the CombinedDepthStencil attachment if you want QPainter to render correctly.
I only attached a depth buffer. The correct initialization would be:
QOpenGLFramebufferObject fbo(size, size, QOpenGLFramebufferObject::CombinedDepthStencil);

How to draw an image inside Rounded Rectangle or inside a shape like circle?

I am new to QT and I want to draw some image inside a shape and image should be croped by a shape.
I use the following code to draw a rectangle for example. How to draw image only inside shape?
QPen pen(Qt::black,penWidth);
pen.setStyle(Qt::DashLine);
QPicture picture;
picture.load("drawing.pic");
painter->setPen(pen);
painter->drawPicture(0,0, picture);
painter->drawRoundedRect( (QRectF(x, y, height, width),radius,radius);
PS. Can I load an image to brush?
Just set a QBrush for painter.
QBrush brush(QPixmap("file.png"));
painter->setBrush(brush);
It will draw the rounded rect filled with file.png image.

How to create a semi transparent shape?

I would like to know how to draw semi-transparent shapes in OpenCV, similar to those in the image below (from http://tellthattomycamera.wordpress.com/)
I don't need those fancy circles, but I would like to be able to draw a rectangle, e.g, on a 3 channel color image and specify the transparency of the rectangle, something like
rectangle (img, Point (100,100), Point (300,300), Scalar (0,125,125,0.4), CV_FILLED);
where 0,125,125 is the color of the rectangle and 0.4 specifies the transparency.
However OpenCV doesn't have this functionality built into its drawing functions. How can I draw shapes in OpenCV so that the original image being drawn on is partially visible through the shape?
The image below illustrates transparency using OpenCV. You need to do an alpha blend between the image and the rectangle. Below is the code for one way to do this.
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int main( int argc, char** argv )
{
cv::Mat image = cv::imread("IMG_2083s.png");
cv::Mat roi = image(cv::Rect(100, 100, 300, 300));
cv::Mat color(roi.size(), CV_8UC3, cv::Scalar(0, 125, 125));
double alpha = 0.3;
cv::addWeighted(color, alpha, roi, 1.0 - alpha , 0.0, roi);
cv::imshow("image",image);
cv::waitKey(0);
}
In OpenCV 3 this code worked for me:
cv::Mat source = cv::imread("IMG_2083s.png");
cv::Mat overlay;
double alpha = 0.3;
// copy the source image to an overlay
source.copyTo(overlay);
// draw a filled, yellow rectangle on the overlay copy
cv::rectangle(overlay, cv::Rect(100, 100, 300, 300), cv::Scalar(0, 125, 125), -1);
// blend the overlay with the source image
cv::addWeighted(overlay, alpha, source, 1 - alpha, 0, source);
Source/Inspired by: http://bistr-o-mathik.org/2012/06/13/simple-transparency-in-opencv/
Adding to Alexander Taubenkorb's answer, you can draw random (semi-transparent) shapes by replacing the cv::rectangle line with the shape you want to draw.
For example, if you want to draw a series of semi-transparent circles, you can do it as follows:
cv::Mat source = cv::imread("IMG_2083s.png"); // loading the source image
cv::Mat overlay; // declaring overlay matrix, we'll copy source image to this matrix
double alpha = 0.3; // defining opacity value, 0 means fully transparent, 1 means fully opaque
source.copyTo(overlay); // copying the source image to overlay matrix, we'll be drawing shapes on overlay matrix and we'll blend it with original image
// change this section to draw the shapes you want to draw
vector<Point>::const_iterator points_it; // declaring points iterator
for( points_it = circles.begin(); points_it != circles.end(); ++points_it ) // circles is a vector of points, containing center of each circle
circle(overlay, *points_it, 1, (0, 255, 255), -1); // drawing circles on overlay image
cv::addWeighted(overlay, alpha, source, 1 - alpha, 0, source); // blending the overlay (with alpha opacity) with the source image (with 1-alpha opacity)
For C++, I personally like the readability of overloaded operators for scalar multiplication and matrix addition:
... same initial lines as other answers above ...
// blend the overlay with the source image
source = source * (1.0 - alpha) + overlay * alpha;