In a game loop that I have, part of the drawing section is:
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
SDL_RenderFillRect(renderer, &blur);
renderer is my renderer and blur is my rect that takes up the whole screen:
SDL_Rect blur;
blur.x = 0;
blur.y = 0;
blur.w = 640;
blur.h = 480;
My problem is that the rect isn't semi transparent. Whenever it draws it, all there is is black.
You cant even see the text that I have underneath. How do I fix this? Does my renderer not support
alpha?
The reason the alpha value isn't affecting anything is because you need to specify which colour blending method you want to use beforehand with this function:
int SDL_SetRenderDrawBlendMode(SDL_Renderer* renderer, SDL_BlendMode blendMode)
The blendMode parameter controls how colour blending works. For alpha color blending, use SDL_BLENDMODE_BLEND:
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 100);
SDL_RenderFillRect(renderer, &blur);
Related
I want to copy multiple surfaces (created with TTF_*) to a single texture, and I can't seem to get that resulting texture to render onto the window with transparency handled correctly.
static void example(void) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
TTF_Init();
SDL_Window* w = SDL_CreateWindow("", 0, 0, 200, 200, 0);
SDL_Renderer* r = SDL_CreateRenderer(w, -1, 0);
TTF_Font* f = TTF_OpenFont(MY_FONT, 100);
SDL_Color c = {.r = 0, .g = 255, .b = 0, .a = 255};
SDL_Surface* s = TTF_RenderGlyph32_Blended(f, 'A', c);
SDL_Texture* t = SDL_CreateTextureFromSurface(r, s);
#ifdef RENDER_COPY
SDL_Texture* t2 = SDL_CreateTexture(
r,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_TARGET,
s->w,
s->h);
SDL_SetRenderTarget(r, t2);
SDL_RenderCopy(r, t, NULL, NULL);
SDL_SetRenderTarget(r, NULL);
t = t2;
#endif
#ifdef RENDER_MEMCPY
SDL_Texture* t2 = SDL_CreateTexture(
r,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
s->w,
s->h);
SDL_Surface* s2;
sdl_try(SDL_LockTextureToSurface(t2, NULL, &s2));
memcpy(s2->pixels, s->pixels, s->w * s->h * sizeof(SDL_Color));
SDL_UnlockTexture(t2);
t = t2;
#endif
#ifdef RENDER_BLEND
SDL_SetTextureBlendMode(t, SDL_BLENDMODE_BLEND);
#endif
SDL_SetRenderDrawColor(r, 255, 255, 255, 255);
SDL_RenderClear(r);
SDL_Rect rect = {.x = 0, .y = 0};
SDL_QueryTexture(t, NULL, NULL, &rect.w, &rect.h);
SDL_RenderCopy(r, t, &rect, &rect);
SDL_RenderPresent(r);
SDL_Event event;
do { SDL_WaitEvent(&event); } while (event.type != SDL_KEYDOWN);
}
Without RENDER_COPY, I get a texture (created via SDL_CreateTextureFromSurface) that blends correctly onto a render target (this is what I want, but with multiple surfaces combined into one texture.)
With RENDER_COPY (i.e. a second texture is created and then copied onto) the background of the texture is black. This is a contrived example since there is only one surface being copied, but I want to copy multiple surfaces to t2.)
With RENDER_BLEND, the black is mostly gone but it's as if the texture was blended onto a black background.
Is there a way to create a texture that can be set completely transparent instead of a solid color? I've also tried to set the pixels directly (RENDER_MEMCPY) but that just ends up being a solid color as it appears the alpha in each pixel is ignored:
SDL version is 2.0.20.
Figured it out. When doing SDL_RenderCopy from the first texture to the second, the blend mode on the first texture should be set to none:
SDL_SetTextureBlendMode(t, SDL_BLENDMODE_NONE);
Now when the second texture is copied (with SDL_BLENDMODE_BLEND) the edges don't have the black artifacts.
I am trying to make a Paint program that enables the user change background, while foreground color (that is used in drawing shapes) is always black.
Now, when the user changes background color, the HBRUSH paints the whole window with the chosen color and consequently clears all the shapes that were drawn.
To solve the problem I made 3 steps (note that the problem lies in the third step):
saveBeforeBackcolorChange() : a function that saves the window to bitmap before backColor is changed
FillRect(hdc, rect, color): paints the whole window with the new color
loadAfterBackcolorChanged(): ought to take only black pixels from the bitmap (which represent drawings) and consider any color transparent
Here is my implementation for the third step:
void loadAfterBackcolorChanged(HDC hdc){
ULONG_PTR m_gdiplusToken;
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
Graphics graphics(hdc);
wchar_t* filePathString = new wchar_t[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, TEMP_FILE.c_str(), -1, filePathString, 4096);
Image image(filePathString);
Gdiplus::ImageAttributes imAtt;
imAtt.SetColorKey(
Color(1, 1, 1),
Color(255, 255, 255),
ColorAdjustTypeBitmap);
graphics.DrawImage(
&image,
Rect(0, 0, image.GetWidth(), image.GetHeight()), // dest rect
0, 0, image.GetWidth(), image.GetHeight(), // source rect
UnitPixel,
&imAtt);
}
Unfortunately, the result is that it considers only white color transparent. Which means that it works well if the background is white (which is the initial backColor). Consequently the user can change backColor successfully only once!
I will be grateful if anyone could help.
I dont exactly understand what renderer is. Can I have multiple renderers or is there always just one?
For example, how can I draw a rectangle with a certain color on a background with a different color using a renderer?
I believe the answer lies in the functions SDL_RenderDrawRect() and SDL_RenderFillRect(). Am I right?
I know how surfaces and bliting works but I dont know what exactly the renderer symbolizes.
If someone could show me how to draw a rectangle, I think i will understand how renderers work.
So far I have this:
#include <SDL.h>
int main(int argc, char* argv[]) {
//Initialization
SDL_Init(SDL_INIT_EVERYTHING);
//Window
SDL_Window *MainWindow = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
640, 480,
SDL_WINDOW_SHOWN
);
//Renderer
SDL_Renderer *Background = SDL_CreateRenderer(MainWindow, -1, 0);
SDL_SetRenderDrawColor(Background, 255, 255, 255, 255);
SDL_RenderClear(Background);
SDL_Delay(3000);
//Clean up
SDL_DestroyWindow(MainWindow);
SDL_Quit();
return 0;
}
for the first part of your question see this SO question.
as to why your code doesnt do much:
you are correct that you need to use either SDL_RenderDrawRect(), or SDL_RenderFillRect().
SDL_RenderDrawRect will draw an unfilled rectangle. SDL_RenderFillRect will be filled (hopefully that is obvious).
With SDL_renderer you need to call SDL_RenderPresent to copy the "scene" to the screen.
...
//Renderer
SDL_Renderer* renderer = SDL_CreateRenderer(MainWindow, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer); // fill the scene with white
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); // the rect color (solid red)
SDL_Rect rect(0, 0, 100, 50); // the rectangle
SDL_RenderFillRect(renderer, &rect);
SDL_RenderPresent(renderer); // copy to screen
SDL_Delay(3000);
...
In C# and XNA, you can create a 1x1 texture like this:
Texture2D white_pixel;
white_pixel = new Texture2D(GraphicsDevice, 1, 1);
white_pixel.SetData<Color[]>(new Color{ Color.White });
// Sorry if I got the syntax wrong, it's been a while
Then later on, you can arbitrarily draw the pixel to any size and color by doing this:
spriteBatch.Begin();
spriteBatch.Draw(white_pixel, new Rectangle(0, 0, width, height), Color.Whatever);
spriteBatch.End();
What is the equivalent in SDL?
SDL_Texture *tex = nullptr;
SDL_CreateTexture(renderer,
Uint32 format, // What do I put here
int access, // and here
1
1);
// Not sure if this is correct
SDL_SetTextureColorMod(tex,
255,
255,
255)
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.w = 10;
rect.h = 10;
SDL_RenderCopy(renderer, tex, nullptr, &rect);
SDL_PIXELFORMAT_RGB24/SDL_PIXELFORMAT_BGR24 for format and SDL_TEXTUREACCESS_STATIC for access would be a good start.
Or you could just draw a colored rectangle directly via SDL_SetRenderDrawColor() and SDL_RenderFillRect().
I have a window created with the WS_EX_LAYERED window style. I am currently drawing onto a memory bitmap using GDI+, and using UpdateLayeredWindow to update the graphical content of my layered window.
Here's a snippet of my code:
void Redraw(HWND hWnd, int width, int height) {
static bool floppy = true;
floppy = !floppy;
HDC hScreenDC = GetDC(HWND_DESKTOP);
HDC hMemDC = CreateCompatibleDC(hScreenDC);
HBITMAP hBmp = CreateCompatibleBitmap(hScreenDC, width, height);
HGDIOBJ hObj = SelectObject(hMemDC, hBmp);
Graphics gfx(hMemDC);
SolidBrush b(Color(254, (floppy ? 255 : 0), (floppy ? 0 : 255), 0));
gfx.FillRectangle(&b, Rect(0, 0, width, height));
BLENDFUNCTION blend;
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
POINT src = { 0, 0 };
SIZE size;
size.cx = width;
size.cy = height;
Assert(UpdateLayeredWindow(
hWnd,
hScreenDC,
NULL,
&size,
hMemDC,
&src,
RGB(0, 0, 0),
&blend,
ULW_ALPHA
));
SelectObject(hMemDC, hObj);
DeleteObject(hBmp);
DeleteDC(hMemDC);
ReleaseDC(HWND_DESKTOP, hScreenDC);
}
When creating my SolidBrush, I specified the value of 254 for the alpha component. This results in a 99.6% opaque fill, which is not what I want.
When I specify 255 as the alpha component, there appears to be no fill; my window becomes completely transparent. This is an issue because I wish to draw shapes that are 100% opaque, but I also wish to draw some that aren't.
There seems to be some qwerks with FillRectangle. This becomes apparent when we observe that using FillEllipse with a SolidBrush whose alpha component is 255, results in the shape being rendered perfectly (opaque).
Here are two work-arounds that I came up with, which each solve the issue for me:
Call FillRectangle twice
SolidBrush b(Color(254, 255, 0, 0));
gfx.FillRectangle(&b, Rect(0, 0, width, height));
gfx.FillRectangle(&b, Rect(0, 0, width, height));
Since the same area is being filled twice, they will blend and create RGB(255, 0, 0) regardless of the content behind the window (it's now 100% opaque). I do not prefer this method, as it requires every rectangle to be drawn twice.
Use FillPolygon instead
Just as with FillEllipse, FillPolygon doesn't seem to have the colour issue, unless you call it like so:
SolidBrush b(Color(255, 255, 0, 0));
Point points[4];
points[0] = Point(0, 0);
points[1] = Point(width, 0);
points[2] = Point(width, height);
points[4] = Point(0, height);
gfx.FillPolygon(&b, points, 4); //don't copy and paste - this won't work
The above code will result in a 100% transparent window. I am guessing that this is either due to some form of optimisation that passes the call to FillRectangle instead. Or - most likely - there is some problem with FillPolygon, which is called by FillRectangle. However, if you add an extra Point to the array, you can get around it:
SolidBrush b(Color(255, 255, 0, 0));
Point points[5];
points[0] = Point(0, 0);
points[1] = Point(0, 0); //<-
points[2] = Point(width, 0);
points[3] = Point(width, height);
points[4] = Point(0, height);
gfx.FillPolygon(&b, points, 5);
The above code will indeed draw a 100% opaque shape, which fixes my problem.
UpdateLayeredWindow() requires a bitmap with pre-multiplied alpha:
Note that the APIs use premultiplied alpha, which means that the red,
green and blue channel values in the bitmap must be premultiplied with
the alpha channel value. For example, if the alpha channel value is x,
the red, green and blue channels must be multiplied by x and divided
by 0xff prior to the call.
You can use Bitmap::ConvertFormat() to convert a bitmap to pre-multiplied (the format is PixelFormat32bppPARGB).