Qt Pixmap Manipulation - c++

I'm attempting to create a process manager based on QPixmaps. These pixmaps are updated via a timer callback at 20Hz, so it appears like they're spinning.
Below is a screenshot of my current window.
The pixmap is a 2D array with [20][14] elements. 20 is the amount of time slices, 14 is the process status indicator. I know, there are only 13 displayed here on the screen.
I'm currently using the painter object and composition modes to add QImages together with the following code:
QImage textImage(mapIcon.size(), QImage::Format_ARGB32);
QImage resultImage(mapIcon.size(), QImage::Format_ARGB32);
QPainter textPainter(&textImage);
textPainter.setCompositionMode(QPainter::CompositionMode_Source);
textPainter.fillRect(mapIcon.rect(), Qt::black);
textPainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
textPainter.setFont(QFont("Arial", 8, QFont::Bold));
textPainter.setPen(QPen(Qt::white));
textPainter.drawText(QRectF(0, 0, mapIcon.width(), mapIcon.height()),
Qt::AlignCenter, name);
textPainter.end();
QPainter resultPainter(&resultImage);
resultPainter.setCompositionMode(QPainter::CompositionMode_Source);
resultPainter.drawImage(0, 0, mapIcon);
resultPainter.setCompositionMode(QPainter::CompositionMode_Exclusion);
resultPainter.drawImage(0, 0, textImage);
resultPainter.setRenderHint(QPainter::SmoothPixmapTransform, true);
resultPainter.setRenderHint(QPainter::HighQualityAntialiasing, true);
runnerIcons[x][y] = QPixmap::fromImage(resultImage);
My question is this: I don't want the inverted color scheme seen on the green labels, I want that color black. The white on black is ok, but the magenta isn't my favorite.
Is this possible in Qt? Or maybe ya'll have some suggestions to alternative solutions?
Thanks in advance.

There are a couple things you can do. You might check out the QPainter::setCompositionMode (mode) method. Maybe one of the other composition modes would be useful to you? Particularly QPainter::CompositionMode_Exclusion or QPainter::RasterOp_SourceXorDestination.
You could also use a complimentary color for the text. Magenta (blue+red) is opposite green on the color wheel. If you made the text magenta, it would probably show up well against both black and green. (I'm not saying it will look great - just that it will show up well.) Or maybe you could make the text 50% gray so that there is contrast in the luminance as well as in the color?
Also, you only have 13 icons, not 14. Not sure if it makes any difference.

Related

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().

MFC displaying png with transparent background

I need a way to display a PNG image with transparent background on a background with gradient color.
I have tried this:
CImage img;
CBitmap bmp;
img.Load(_T(".\\res\\foo.png"));
bmp.Attach(img.Detach());
CDC dcStatus;
dcStatus.CreateCompatibleDC(&dc);
dcStatus.SelectObject(&bmp);
dcStatus.SetBkColor(TRANSPARENT);
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcStatus, 0, 0, SRCCOPY);
bmp.DeleteObject();
but foo.png gets black background wherever it is transparent in original image.
I did try to do a new bitmap that was painted with transparent color and did all possible operations on it, but that didn't help. Sample of one permutation:
CImage img;
CBitmap bmp;
img.Load(_T(".\\res\\foo.png"));
bmp.Attach(img.Detach());
CBitmap bmpMaska;
bmpMaska.CreateBitmap(14, 14, 1, 1, NULL);
CDC dcStatus;
dcStatus.CreateCompatibleDC(&dc);
dcStatus.SelectObject(&bmp);
CDC dcMaska;
dcMaska.CreateCompatibleDC(&dc);
dcMaska.SelectObject(&bmpMaska);
dcMaska.SetBkColor(dcStatus.GetPixel(0, 0));
//TODO: Bitmap ni transparent
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcMaska, 0, 0, SRCCOPY);
dc.BitBlt(rectText.left + 250, rectText.top, 14, 14, &dcStatus, 0, 0, SRCAND);
bmp.DeleteObject();
bmpMaska.DeleteObject();
This did not do the trick. Either, there was all black square on the screen, or the result was the same as original.
I have also checked AlphaBlend API, but my code must be pure MFC + C++ witthout any additional APIs.
[Edit]: Company policy is as little APIs as possible. The code is supposed to run on embedded windows systems in real time.
[Edit 2]: I am not bound to PNG image format, meaning, anything that will display as transparent, goes.
Please, tell me what am I doing wrong?
Convert the png to a 32 bit bmp file. Google 'AlphaConv' and use that. PNG's are a PITA to work with - several API's claim to support them but actually don't, or only partially, or only on some platforms etc.
Load your 32-bit bmp using CBitmap::LoadBitmap (from resources - this is by far the easiest)
Then use CDC::AlphaBlend() to draw your bitmap. TransparentBlt() doesn't work with an alpha channel - it's just either draw the pixel or not. AlphaBlend is part of the win32 API, and you could still investigate CImage::AlphaBlend if you'd like, but that has a bug somewhere (I forgot what exactly) so I always use the raw ::AlphaBlend.
But beware - you need to premultipy the alpha channel for correct display. See my answer on How to draw 32-bit alpha channel bitmaps? .
No variation of what you described will work. You need another API to get this to work. Or, you could use GetDIBits and do your own version of ::AlphaBlend() :)
Based on what you're asking for (simply some set of pixels being transparent) it's probably easiest to use a 24- or 32-bit BMP, and simply pick a color to treat as transparent. Pre-process your picture to take any pixel that precisely matches your transparent color and increase change the least significant bit of one of the channels.
Given 8 bits per channel, this won't normally cause a visible change. Then draw the pixels you want transparent in one exact color. This is easy to do in something like a paint program.
You can then draw that to your DC using TransparentBlt. That lets you specify the color you want to treat as transparent.

ClearType ruins transparency

I have a bitmap which background needs to be replaced with part of another bitmap. Everything works fine until I enable ClearFont on my WindowsXP.
In order to explain my problem better, let us label first bitmap as bmpDestination and second as bmpSource.
Here is how the bmpSource looks like :
Here is how bmpDestination looks like :
When ClearType is off, here is how the correct result looks like :
And here is the incorrect result of their combining when ClearType is on:
ClearType alters some parts of the text background color, so they aren't white anymore ( RGB( 255, 255, 255 ) ) but a combination of white and text color.
I am using BitBlt() and monochrome bitmap to create a mask, and to simulate transparency. I have tried using TransparentBlt() too, but got the same result.
How can I combine bmpSource and bmpDestination, when ClearType is enabled, so I can create correct result like above ?
Thank you for your help.
Best regards.
Render the treeview with black text on a white background. Use a font with grey-scale anti-aliasing. Don't use ClearType anti-aliasing. I'm moderately sure you can achieve this with one of the fdwQuality parameters to CreateFont, but I wouldn't swear to it.
Each pixel will have a shade of grey between white and black. You can interpret this as transparency. White is fully transparent; black is fully opaque. Use this information to create a bitmap with transparency. Render this transparent bitmap over your multi-coloured background.

Difference between fill color and background color

I am porting an application from Windows to Mac OS X. Here, I have one confusion between the use of different terms.
On Windows, we use SetBkColor to set background color of a device context.
On Mac OS X, there is setFill to set fill color.
Is there any difference between this background color of Windows and fill color of Mac OS X?
For stroke clear (by setStroke), I think on Windows, same effect is achieved by CreatePen for lines and SetTextColor for texts. Is this concept is okay?
Both native Windows development and Core Graphics on iOS/Mac OS use the so called 'painter's model' of drawing. Just like actual painting, you select a color for your pen or brush and everything you draw, fill, what-have-you from that point until you change it will use that color. On the Mac, more specifically, you set stroke for such things as text and borders, and fills for methods that fill. You have to set each separately as each accomplishes something different.
SetBkColor would be different because it fills into the background, on Mac or iOS, you would instead set the fill color and then use a drawing method to fill a rect -- and usually this would all be done by overriding a view's drawRect method. For example, here's one way to do that:
- (void)drawRect:(NSRect)rect
{
CGContextRef myContext = [[NSGraphicsContext currentContext] graphicsPort];
// ********** Your drawing code here **********
CGContextSetRGBFillColor (myContext, 1, 0, 0, 1); // set my 'brush color'
CGContextFillRect (myContext, CGRectMake (0, 0, 200, 100 )); // fill it
CGContextSetRGBFillColor (myContext, 0, 0, 1, .5); // set my brush color
CGContextFillRect (myContext, CGRectMake (0, 0, 100, 200)); //fill it
}
Drawing is done back to front, so, if you wanted to set the background to a certain color, you would simply make that the first operation and fill the full window/view rectangle with whatever color you like.
Have a look at the Quartz 2D drawing guide for further examples. If you are coming from Windows, you will find Quartz/Core Graphics to have a very comparable, and in my mind richer, set of drawing capabilities. (The above example is from this guide)
https://developer.apple.com/library/mac/documentation/graphicsimaging/conceptual/drawingwithquartz2d/dq_context/dq_context.html

Why would GDI+ colours vary based off whether a tooltip is visible?

I'm displaying a bitmap using GDI+. After loading the bitmap from a DLL resource I set the background colour (blue - #0000FF) to transparent using TransparentBlt. On Windows Vista and later this works as expected.
However, on a Windows XP system we're testing on this only works when any tooltip (e.g. the "title" property in IE, or Windows Explorer's tooltip shown when hovering the mouse over a file, etc) is displayed. The rest of the time the background colour is still blue.
Has anyone encountered this before, or know of a way to stop this occurring and for the blue to be properly made transparent?
Edit: After further investigation I found that setting colour depth in Windows XP to 16 bit colours instead of 32 bit colours caused TransparentBlt to start working normally again. Obviously this isn't an ideal solution, specifying what colour depth must be used, but does this give any hint to what might be happening?
Edit2: Code sample included.
m_pGDIBitmap = new Gdiplus::Bitmap(_Module.m_hInst, MAKEINTRESOURCE(lImageResource));
m_hMemDC = CreateCompatibleDC(hdc);
Gdiplus::Graphics myGraphics(m_hMemDC);
myGraphics.DrawImage(m_pGDIBitmap,
Gdiplus::Rect(0, 0, m_pGDIBitmap->GetWidth(), m_pGDIBitmap->GetHeight()),
0,
0,
m_pGDIBitmap->GetWidth(),
m_pGDIBitmap->GetHeight(),
Gdiplus::UnitPixel, &imAtt);
SetStretchBltMode(hdc, HALFTONE);
SetBrushOrgEx(hdc, rcBounds.left, rcBounds.top, NULL);
TransparentBlt(hdc, rcBounds.left, rcBounds.top, iScaledWidth, iScaledHeight, m_hMemDC, 0, 0, iBitmapWidth, iBitmapHeight, GetPixel(m_hMemDC, 0, 0));
You must show some sample code - the code that loads the bitmap and the code that blits it to display.
From the symptoms that you describe, my guess is that you load the bitmap not in its native format, but in the current display format. This means, that when the bit depth of the bitmap differs from the bit depth of the display, an automatic color space conversion is made. When this happens, the color that you provide to TransparentBlt is actually different from the color in the bitmap.
Possible alternative solutions:
Make sure that the bitmap is loaded in its native format without conversion.
Allow the conversion to take place, but instead of providing a hardcoded color value to TransparentBlt, make a GetPixel of a known "transparent" pixel of the bitmap (like top-left), and provide this value to the TransparentBlt.
What I ended up doing was more of a workaround, but it did work. I changed the background colour to black and added the following code before the DrawImage call:
Gdiplus::ImageAttributes imAtt;
imAtt.SetColorKey(Gdiplus::Color(0, 0, 0), Gdiplus::Color(0, 0, 0), Gdiplus::ColorAdjustTypeBitmap);
For some reason using this with blue as the background didn't work and using TransparentBlt on its own with either colour didn't work, but the combination applied the transparency correctly on the various OSes and colour depths that I tried.
If I had access to a paint program that supported bitmap files with an alpha channel, i.e. 32 bit bitmaps, I suspect making the background explicitly transparent and then using AlphaBlend would have worked as well but I didn't have the ability to try that at the time.