So, i have a cursor sprite:
I load this sprite with:
sf::Cursor cur;
sf::Image cur_default;
if(!cur_default.loadFromFile("assets/sprites/cursors/cursor_default.png"))
{
std::cerr << "ERROR: unable to load file 'assets/sprites/cursors/cursor_default.png'\n";
return -1;
}
cur.loadFromPixels(cur_default.getPixelsPtr(), cur_default.getSize(), cur_default.getSize() / 2U);
When i was testing my code, I realized that the left size of the sprite was white, and that the colors didn't show up (replaced the white/gray with blue). After resizing the image to 32x32 pixels, it seems as if there are random while lines in my cursor. Unfortunately, I could not take a screenshot of the behavior because it wasn't showing up. I'm aware that according to the SFML wiki:
On Unix, the pixels are mapped into a monochrome bitmap: pixels with an alpha channel to 0 are transparent, black if the RGB channel are close to zero, and white otherwise.
However, it states unix not unix like, so I assume that is not the case for linux, but feel free to correct me if I'm wrong. I'm confused on why there are random white lines in my sprite, and how to fix it (AND also why the colors are not working correctly).
Related
I am trying to use QLabel as a powerful tool to render some text with CSS into a texture. After I obtain an image, a texture is formed in OpenGL and the original Qt object is discarded. Afterwards I use the texture as any other texture in plain OpenGL (without Qt) rendering pipeline.
However, I am having problems handling the transparency of the background. Something - probably some Qt setting that I am not aware of - seems to be messing up my settings.
After a bit of simplification, my texture is produced like this:
QLabel label;
label.setAttribute(Qt::WA_TranslucentBackground, true);
const QString styleSheet{"color : #00bbbb; background-color : rgba(0,0,0,0);"
"font-family: 'Calibri'; text-decoration: none; margin: 5px;"};
label.setWordWrap(true);
label.setAutoFillBackground(false);
label.setStyleSheet(styleSheet);
QFont font = label.font();
font.setPointSizeF(12.0f);
label.setFont(font);
// set text
label.setText(QString("The quick brown fox jumps over the lazy dog"));
// render
label.adjustSize();
label.updateGeometry();
std::unique_ptr<QImage> imgTexture = std::make_unique<QImage>(label.size(), QImage::Format_RGBA8888);
QPainter painter(imgTexture.get());
label.render(&painter);
uint8_t *bits = imgTexture->bits();
std::cout << int(bits[0]) << " " << int(bits[1]) << " " << int(bits[2]) << " " << int(bits[3]) << std::endl;
The output - the value of the top-left pixel of the produced image - is:
205 205 205 205
and not 0 0 0 0 as I expected. Thus, the problem is already at this point and not later in my OpenGL handling of the texture. Ultimately my output is:
As seen, the background is not entirely transparent as expected.
Update
As per G.M.'s suggestion I tried setCompositionMode (with no effect), but also other painter settings. Apparently setting QPainter::setBackgroundMode to Qt::OpaqueMode steps one step further:
The Qt manual says:
Qt::TransparentMode (the default) draws stippled lines and text without setting the background pixels. Qt::OpaqueMode fills these space with the current background color.
so, it seems the default is to not change the original pixels of the output image anywhere where letters are not present. So, the transparent (0,0,0,0) is not drawn, and the previous color (apparently 205, 205, 205, 205 for some reason) remains unchanged.
Forcing drawing the background updates all pixels but only in the neighbourhood of the letters. I now need to figure out how to force clearing all pixels to the color specified in CSS.
Update Apparently it is not as simple as it seems. I tried painter.eraseRect(0, 0, width, height); but this clears the rectangle into white, ignoring the CSS settings.
To combine the results of experiments and some comments. The behavior is a combination of:
When QImage is constructed with some image area, that area is uninitialized.
QImage::QImage(int width, int height, QImage::Format format)
Constructs an image with the given width, height and format.
A null image will be returned if memory cannot be allocated.
Warning: This will create a QImage with uninitialized data. Call fill() to fill the image with an appropriate pixel value before drawing onto it with QPainter.
On Visual Studio in Debug mode, uninitialized area is initialized with 0xCD. In Release it would be some garbage
By default, when drawing text with QPainter, the background remains unchanged. The painter just adds the glyphs to whatever was previously present on the target image (in our case: the uninitialized area).
void QPainter::setBackgroundMode(Qt::BGMode mode)
Sets the background mode of the painter to the given mode
Qt::TransparentMode (the default) draws stippled lines and text without setting the background pixels. Qt::OpaqueMode fills these space with the current background color.
Note that in order to draw a bitmap or pixmap transparently, you must use QPixmap::setMask().
So, in order for background pixels to be drawn, as set in QLabel's CSS, the mode needs to be changed to Qt::OpaqueMode. This however draws only around glyphs, not the whole area unfortunately.
We need to manually clear the whole area first.
Image can be cleared via QPainter::fillRect:
void QPainter::fillRect(int x, int y, int width, int height, const QColor &color)
This is an overloaded function.
Fills the rectangle beginning at (x, y) with the given width and height, using the given color.
This function was introduced in Qt 4.5.
but there is a caveat - all QPainter operations are blended with the underlying image. By default it takes the alpha channel of the drawing color into account. Thus, if you paint with (0,0,0,0) you get... no change. The blending operation is controlled by:
void QPainter::setCompositionMode(QPainter::CompositionMode mode)
Sets the composition mode to the given mode.
Warning: Only a QPainter operating on a QImage fully supports all composition modes. The RasterOp modes are supported for X11 as described in compositionMode().
See also compositionMode().
enum QPainter::CompositionMode
Defines the modes supported for digital image compositing. Composition modes are used to specify how the pixels in one image, the source, are merged with the pixel in another image, the destination.
[...]
When a composition mode is set it applies to all painting operator, pens, brushes, gradients and pixmap/image drawing.
Constant Value Description
QPainter::CompositionMode_SourceOver 0 This is the default mode. The alpha of the source is used to blend the pixel on top of the destination.
QPainter::CompositionMode_DestinationOver 1 The alpha of the destination is used to blend it on top of the source pixels. This mode is the inverse of CompositionMode_SourceOver.
QPainter::CompositionMode_Clear 2 The pixels in the destination are cleared (set to fully transparent) independent of the source.
QPainter::CompositionMode_Source 3 The output is the source pixel. (This means a basic copy operation and is identical to SourceOver when the source pixel is opaque).
QPainter::CompositionMode_Destination 4 The output is the destination pixel. This means that the blending has no effect. This mode is the inverse of CompositionMode_Source.
[...] (30 more modes...)
So, the default SourceOver needs to be replaced by Source before calling fillRect.
Clearing can be also done by QImage::fill. Much easier and no mess with draw modes!
Unfortunately, either solution (QImage::fill or QPainter::fillRect) requires the specification of the background color explicitly. It cannot be just read from QLable's CSS.
P.S. I don't know how to blockquote a table :(
I have a Mat containing text with a black background and a Mat containing a background. I want to merge the 2 Mat's so the text sits ontop of the background. Both Mats are BGR (CV_8UC3) and the same size (cols and rows).
Whats the OpenCV way of doing this?
I'm aware of a couple of functions but |= and += are changing the text colour and addWeighted() only works if I want one image to be slightly transparent. I just want to do a complete merge of the two (except not the black pixels in the text image). I'm looking for an OpenCV function rather than pixel editting myself, well a LUT would be good if thats an option.
Try this:
sourceText = yourTextImage;
sourceBackground = yourBackgroundImage;
cv::Mat textTransparent;
cv::inRange(sourceText, cv::Scalar(0,0,0), cv::Scalar(0,0,0), textTransparent);
cv::imshow("transparent pixel", textTransparent); // this should show all the transparent pixel as being white. If that's not the case, adjust the range.
std::cout << "is the transparency mask ok? Press any key with openCV window focus." << std::endl;
cv::Mat result = sourceBackground.clone();
sourceText.copyTo(sourceBackground, 255-textTransparent); // copy pixels, using the created mask.
instead you could copy the transparent pixels from the sourceBackground to the sourceText which is a bit faster because you don't need the 255-textTransparent...
Could not test the code, please tell me if there are bugs.
I have started to create a paint program that interacts with drawing tablets. Depending on the pressure of the pen on the tablet I change the alpha value of the line being drawn. That mechanism works.
Thin lines look decent and it looks a real sketch. But since I am drawing lines between two points (like in the Qt scribble tutorial) to paint there is an alpha overlap between the line joints and it is very noticeable for thick strokes.
This is the effect with line to line conjuction:
As you can see, there is an ugly alpha blend between the line segments.
In order to solve this I decided to use a QPainterPath to render lines.
Two problems with this:
A long, continuous, thick path quickly lags the program.
Since the path is connected it acts as one, so any change to the alpha value affects the the entire path(which I don't want since I want to preserve a blending effect).
The following images use a QPainterPath.
The blend effect I want to keep.
The following image shows the 2nd problem which changes the alpha and thickness of the entire path
The red text should read: "if more pressure is added without removing the pen from the tablet surface the line thickens" (and alpha becomes opaque)
Another thing is that with this approach I can only get a blending trail from a dark to light (or thick to thin path width) but not light to dark. I am not sure why this effect occurs but my best guess is that it has to do with the line segments of the path updating as whole.
I did make the program increase/decrease alpha and line thickness based on the pressure of the pen on the tablet.
The problem is that I want to render lines without the alpha overlap and QPainterPath updates the entire path's alpha and thickness which I don't want.
This is the code that creates the path:
switch(event->type()){
case QEvent::TabletPress:
if(!onTablet){
onTablet = true;
//empty for new segment
freePainterPath();
path = new QPainterPath(event->pos());
} break;
case QEvent::TabletRelease:
if(onTablet)
onTablet = false;
break;
case QEvent::TabletMove:
if(path != NULL)
path->lineTo(event->pos());
if(onTablet){
//checks for pressure of pen on tablet to change alpha/line thickness
brushEffect(event);
QPainter painter(&pixmap);
//renders the path
paintPixmap(painter, event);
} break;
default:;
}
update();
The desired effect that I want as a single path (image created with Krita paint program):
To emulate the Krita paint program:
Keep a backup of the original target surface.
Paint with your brush onto a scratch surface that starts out completely transparent.
On that surface, your composting rule is "take maximum opacity".
Keep track of the dirty regions of that surface, and do a traditional composite of (scratch surface) onto (original target surface) and display the result. Make sure this operation doesn't damage the original target surface.
Now, you don't have to keep the entire original target surface -- just the parts you have drawn on with this tool. (A good tile based lazy-write imaging system will make this easy).
Depending on the segment size you are drawing with, you may want to interpolate between segments to make the strength of the brush be a bit less sharp. The shape of your brush may also need work. But these are independent of the transparency problem.
As for the Qt strangeness, I don't know enough Qt to tell you how to deal with the quirks of Qt's brush code. But the above "key-mask" strategy should solve your alpha overlap problem.
I do not know how to do this in Qt. Glancing at the Qt compositing modes I don't see an obvious way to say "take maximum" as the resulting alpha. Maybe something involving both color and alpha channels in some clever way.
I know this question is very old, and has an accepted answer, but in case someone else needs the answer, here it is:
You need to set the composition mode of painter to source. It draws both source and destination right now.
painter.setCompositionMode(QPainter::CompositionMode_Source);
If you want your transparent areas to show through underlying drawings, you need to set the composition mode of your result back to CompositionMode_SourceOver and draw over destination.
I don't know if you still look for an answer, but I hope this helps someone.
I am using a LPDIRECT3DTEXTURE9 to hold my image.
This is the function used to display my picture.
int drawcharacter(SPRITE& person, LPDIRECT3DTEXTURE9& image)
{
position.x = (float)person.x;
position.y = (float)person.y;
sprite_handler->Draw(
image,
&srcRect,
NULL,
&position,
D3DCOLOR_XRGB(255,255,255));
return 0;
}
According to the book I have the RGB colour shown as the last parameter will not be displayed on screen, this is how you create transparency.
This works for the most part but leaves a pink line around my image and the edge of the picture. After trial and error I have found that if I go back into photoshop I can eliminate the pink box by drawing over it with the pink colour. This can be see with the ships on the left.
I am starting to think that photoshop is blending the edges of the image so that background is not all the same shade of pink though I have no proof.
Can anyone help fix this by programming or is the error in the image?
If anyone is good at photoshop can they tell me how to fix the image, I use png mostly but am willing to change if necessary.
edit: texture creation code as requested
character_image = LoadTexture("character.bmp", D3DCOLOR_XRGB(255,0,255));
if (character_image == NULL)
return 0;
You are loading a BMP image, which does not support transparency natively - the last parameter D3DCOLOR_XRGB(255,0,255) is being used to add transparency to an image which doesn't have any. The problem is that the color must match exactly, if it is off even by only one it will not be converted to transparent and you will see the near-magenta showing through.
Save your images as 24-bit PNG with transparency, and if you load them correctly there will be no problems. Also don't add the magenta background before you save them.
As you already use PNG, you can just store the alpha value there directly from Photoshop. PNG supports transparency out of the box, and it can give better appearance than what you get with transparent colour.
It's described in http://www.toymaker.info/Games/html/textures.html (for example).
Photoshop is anti-aliasing the edge of the image. If it determines that 30% of a pixel is inside the image and 70% is outside, it sets the alpha value for that pixel to 70%. This gives a much smoother result than using a pixel-based transparency mask. You seem to be throwing these alpha values away, is that right? The pink presumably comes from the way that Photoshop displays partially transparent pixels.
I'm using the following code to resize a bitmap using GDI+ in C. I'm getting gray values for the top edge even for images where that area should be white. The problem disappears when the interpolation is nearest neighbor. However, I tried bicubic interpolation with ImageMagick and it seems fine. Any limitations problems I should know of? Is my method of scaling wrong somehow? Thanks!
(inputs:
destSize : destination Gdiplus::Size
m_pBitmap : source bitmap)
Rect destRect(Point(0,0), destSize);
Bitmap *pBitmap24BPP = new Bitmap(destSize.Width, destSize.Height, PixelFormat24bppRGB);
pBitmap24BPP->SetResolution(m_pBitmap->GetHorizontalResolution(), m_pBitmap->GetVerticalResolution());
Graphics *pGraphics = Graphics::FromImage(pBitmap24BPP);
pGraphics->SetInterpolationMode(InterpolationModeHighQualityBilinear);
pGraphics->DrawImage(m_pBitmap, destRect, 0, 0, m_pBitmap->GetWidth(), m_pBitmap->GetHeight() , UnitPixel, NULL);
//cleanup
i've been seeing similar things. i've not had time to research it, but i have a feeling it is because the GDI+ image resizer is mistakenly using pixels values "off the top edge", which would be all zero - making it black.
Proper image resizing should recognize that you've reached the edge of the image, and handle it appropriately.