As part of a larger project i'm trying to draw basic shapes on a QPixmap but the generated image always appears blurry ie. the circle is not a pure red circle, the edges are blurred and the colour is not true red as expected. This becomes an issue later on with what I need the image for. Here's the relevant code snippet, i've played around with antialiasing options and render hints but i've not had any luck. Any help would be much appreciated!
QPixmap pixmap(QSize(300,300));
QPainter painter( &pixmap);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.fillRect( QRectF(0, 0, 300, 300),Qt::white);
painter.setPen(Qt::red);
painter.drawEllipse( QPoint(150,150), 75, 75);
painter.setPen(Qt::green);
QLineF line(30, 30, 30, 270);
painter.drawLine(line);
pixmap.save("test1.jpg", "jpg", 100);
I've been asked to include an image to show the issue, here's a close up of the image which shows the green line not being true green and the red circle not being true red and with blurred edges.
The problem that you have with the red color is that Qt by default uses chroma color subsampling (2x2 blocks) when saving to JPG format.
The only way to avoid this problem is using another format. Saving the same image with PNG format returns a pure red circle (255,0,0).
Regarding to the blurry circle, it occurs because of QPainter::Antialiasing RenderHint. In your case, not using any renderHints shows a sharp image. Below you can see your image with pure red color (PNG format) and sharp edges (not RenderHints). Take into account that the blurriest approach will be using QPainter::Antialiasing so avoid it if you are aiming to image sharpeness. Depending on your needs you could try QPainter::HighQualityAntialiasing RenderHint, but nothing else.
Related
I am applying a canvas as a texture to a plane. And it looks fine (The background is white by default) if I am displaying it within a browser itself.
But as I change to Virtual Reality Mode the background of the canvas is changing to black.
I know that i can place a white rect to have a white background, but this is not feasible due to performance issues.
Are there any other solutions to tackle this problems?
Thanks!
Try filling in the canvas as white (fillRect), or adding a background to the scene itself (<a-sky color="#FFF">). Let me know if that works, or provide a JSFiddle for us to play around.
In short
I'm trying to get the text in my QLabel to paint as if it were over a black background, ignoring the real background colour.
In depth
I have a QLabel in a green coloured widget, the text is anti-aliased and white, the anti-aliasing softens the edges of the text by blending with the green background colour.
Hardware detects the exact green colour and uses it like a green screen to display some techno-wizardry without also drawing over my software overlay, it makes the background look black.
The anti-aliased text has a fuzzy green halo where it has slightly changed the green colour and hardware doesn't overwrite it.
I want the text to be draw as if it were over a black background
What I've tried
I have tried using a black opaque QGraphicsDropShadowEffect to be drawn under the text, thus giving the text a black background to draw on, this led to a bigger green halo around the text.
I have tried setting various QBrushes in the label's QPalette for the Qt::WindowText and Qt::Base ColorRoles.
I have re-implemented the paintEvent and I am trying to use style()->drawItemText(... ) to avoid re-implementing all the helpful alignment code.
I have tried intercepting the QStyleOpt parameter and setting its QPalette's background colour to black, however that didn't change anything, I still get fuzzy, greenish text.
I have tried various QBrush and QPen colours in the QPainter.
I'm really close using a QPainterPath in an overidden paintEvent function (in a class extending QLabel) where I paint a black path of the text behind the text, then I call QLabel::paintEvent and let it draw the text over the top. The issue here is that in some cases the path is clipped to smaller than the widgets rect() on the left hand side, scope for another question I think!
What I could try but haven't worked out yet
I could give the text a black outline as per this question however I'm using a QWidget not a QGraphicsView text item, and then the black outline would still probably blend with the green background.
Compromises I've already thought of and want to avoid
I could set the QLabel background to black, leaving large rectangles of black over the resulting overlay, unfortunately the labels are often much larger than the text being displayed.
I could stop the font being anti-aliased, this looks quite ugly and it stops all fonts being uniform across the product.
I dont know if I am using the correct term here. However this is what I am trying to achieve and I would like some suggestions on how I could achieve that. I want to have a circle with border visible. Now here is the hard part and something I dont even know how to start with. I want to manipulate the circle in such a way that the borders of the circle are visible and its center is not (i.e Pretty much that it has a hole in it and would show what ever is placed under it)I would then like to have another image placed under the circle such that only the part of the image that is under the transparent part of the circle is shown the parts outside the transparent boundary of the circle become invisible. Any suggestions on how I could achieve this. It seems that googling isnt helping me.
I would suggest the alternative way for unmasking a circular area of an image. You can specify the clip region - the area where you need to perform painting. For example:
[..]
QPainter painter(this);
// Sample circular area.
QRegion r(QRect(100, 100, 200, 200), QRegion::Ellipse);
painter.setClipRegion(r);
[..]
painter.drawImage(0, 0, image);
[..]
This will draw only those parts of your image that are inside of the circle with radius 200. All the rest pixels will be hidden.
You can handle mouse move event to move this "circle" over the image like a loupe.
UPDATE
Below is the sample code that generates an image with circular mask and insert it into the label:
QPixmap target(500, 500); // the size may vary
QPixmap source("image.png");
QPainter painter(&target);
QRegion r(QRect(100, 100, 200, 200), QRegion::Ellipse);
painter.setClipRegion(r);
painter.drawPixmap(0, 0, source);
QLabel l;
l.setPixmap(target);
l.show();
You might want to have a look at the Composition Example.
In short you could draw the first image and then use one of the Composition Modes to draw the second image on top (or the other way around). Make sure to convert the images to ARGB32 before using them.
To make the inner Part of the Circle transparent you can adjust the Alpha Channel accordingly.
Here is a small Example using Composition mode:
QPainter p(&imageCircle);
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
p.drawImage(image);
p.end()
Here you can find the Qt Documentation of QPainter.
I would like to change the stock cursor with a translucent one, a simple filled circle, of various sizes, depending on the level of zoom in the underlying widget (say, RGBA = 200, 200, 200, 128).
Is this at all possible with Qt? If not, is it a limitation in Qt or the underlying libs? Do you have suggestions as to how this could be accomplished by other means, e.g., hiding the cursor and overlaying a transparent pixmap at the cursor position (albeit slower)? TIA
QCursor can take a QPixmap which does support alpha channel. So I don't see why it can't be done.
I just figured this out for a project of my own. I did it with this code in the constructor of the relevant widget:
m_LPixmap = new QPixmap(32,32);
m_LPixmap->fill(Qt::transparent); // Otherwise you get a black background :(
QPainter painter(m_LPixmap);
QColor red(255,0,0,128);
painter.setPen(Qt::NoPen); // Otherwise you get an thin black border
painter.setBrush(red);
painter.drawEllipse(0,0,32,32);
m_Cursor = QCursor(*m_LPixmap);
setCursor(m_Cursor);
I am displaying a JPG in a C++ CWnd window using GDI+. The JPG has a pure white background, 0xffffff, but when displayed using graphics.DrawImage, the background is off-white with a mix of pixel colors such as 0xfff7f7, 0xf7fff7, 0xf7f7f7. Below is the code, I have tried various settings such as CompositingMode, SmoothingMode, etc. The image is not scaled.
The weird thing is that the background color is different depending on other non-white content in the image. If I make a simple all white JPG, then it works, or even a mostly white with just some black text. Comparison of images are shown below.
CClientDC dc(this);
Gdiplus::Graphics graphics(dc);
Gdiplus::Bitmap* bmp = Gdiplus::Bitmap::FromFile( L"c:\\test.jpg" );
graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQuality);
//graphics.SetCompositingQuality(Gdiplus::CompositingQualityHighQuality);
graphics.SetCompositingQuality(Gdiplus::CompositingQualityDefault);
graphics.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
//graphics.SetSmoothingMode( Gdiplus::SmoothingMode::SmoothingModeDefault );
graphics.DrawImage(bmp, 0, 0, bmp->GetWidth(), bmp->GetHeight() );
Here I have text and some blending only on the left side of the image (no alphas, this is JPG) . Everything to the right is pure white. You can see the background is all grey.
Here I started removing the internal content (only on the left side). After a certain point the entire background starts displaying white. ???
It doesn't really matter which part of the image area I remove before it starts displaying white, as long as I remove a large portion of it. The same occurs for pngs.
Here is the original test.jpg image...
I am answering my question with the solution that I found. It seems that using graphics.DrawImage directly on the passed HDC has some issues in my case. If I use a memory DC for the initial drawing, then BitBlt it on the HDC, then it works.
I also had some problems with PNG and transparency. Using the solution below, I was able to solve this problem as well. My PNG was loaded from a stream using Bitmap::FromStream. The alpha channel was lost and I was trying different attempts using LockBits and re-creating the bitmap with PixelFormat32bppARGB, as well as Cloning. I was able to get something to work (after a lot of effort and extra code), but it still had the grey background problem that I asked here.
In my case, I have a known solid background color for the transparent areas. I used Bitmap.GetHBITMAP and passed the background color. The bitmap was then drawn on the memory DC first. Here I was able to solve both of my problems!
Gdiplus::Bitmap* bmp = Gdiplus::Bitmap::FromFile( L"c:\test.jpg" )
Gdiplus::Color backColor( 0xff, 0xff, 0xff );
HBITMAP hBmp;
bmp->GetHBITMAP( backColor, &hBmp );
CDC bitmapDC;
bitmapDC.CreateCompatibleDC(hdc); // pass original HDC for drawing
HBITMAP oldBmp = bitmapDC.SelectBitmap(hBitmap);
::BitBlt( hdc, x, y, cx, cy, bitmapDC.m_hDC, 0, 0, SRCCOPY );
bitmapDC.SelectBitmap(oldBmp);
DeleteObject( hBmp );
If anyone knows, I would be interested why this fixes the problem.
How did you confirm the JPG background is truly white? JPG implements compression that can vary the color of pixels. If there are mixed colors, then there can be certain types of blending and mixing that are part of that compression.
Can you show us the original image?