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().
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.
In order to make buttons, I create and render texture that way:
typedef struct{
SDL_Rect pos;
SDL_Texture* texture;
int hovered;
} button;
button getButton(int x, int y, char * label, TTF_Font* font, SDL_Color color){
button btn;
btn.hovered = false;
btn.pos.x = x;
btn.pos.y = y;
SDL_Surface* surface = TTF_RenderText_Solid(font, label, color);
btn.pos.w = surface->w;
btn.pos.h = surface->h;
btn.texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);
return btn;
}
void drawButton(button btn){
SDL_RenderCopyEx( renderer, btn.texture, NULL, &btn.pos, 0, NULL, SDL_FLIP_NONE);
if(btn.hovered){
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 0x00);
SDL_RenderDrawRect(renderer, &btn.pos);
}
The problem is that I get texture which size equals one of label. How can I increase texture pixel size without stretching it i.e. add blank spaces to the side of it?
Something like
void drawButton(button btn){
SDL_RenderCopyEx( renderer, btn.texture, NULL, &btn.pos, 0, NULL, SDL_FLIP_NONE);
if(btn.hovered){
int padding = 10;
SDL_Rect pos = {btn.pos.x - padding, btn.pos.y - padding,
btn.pos.w + 2*padding, btn.pos.h + 2*padding };
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 0x00);
SDL_RenderDrawRect(renderer, &pos);
}
}
That way only the size of the rectangle changes, obviously I just pulled 10 for the size of padding out of thin air, you'll want to pick something appropriate yourself.
Found a way to do it. To enlarge the texture you create surface representing background of button, then combine them:
button getButton(int x, int y, char * label, TTF_Font* font, SDL_Color color){
button btn;
btn.hovered = false;
btn.pos.x = x;
btn.pos.y = y;
SDL_Surface* surface = TTF_RenderText_Solid(font, label, color);
SDL_Surface* back = SDL_CreateRGBSurface(0, surface->w+10, surface->h+10,
32, 0, 0, 0, 0);// create a black background
SDL_Rect t = {5, 5, back->w, back->w}; // place in a background to place label
SDL_BlitSurface(surface, NULL, back, &t); // combining surfaces
btn.pos.w = back->w;
btn.pos.h = back->h;
btn.texture = SDL_CreateTextureFromSurface(renderer, back);
SDL_FreeSurface(surface);
return btn;
}
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);
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).
I'm working with SDL and OpenGL.
I'm using SDL ttf for create a surface with the text, then create a texture with that surface.
I'm trying to apply an alpha channel (opacity) to the text, I'm using this way:
SDL_Surface * fontsurf = TTF_RenderUTF8_Blended(font,buff_split.at(i).c_str(),color);
SDL_PixelFormat *format = fontsurf->format;
SDL_Surface* newSurface = SDL_CreateRGBSurface( SDL_SRCALPHA,
fontsurf->w, fontsurf->h, 32,
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 );
Uint32 alpha = 0;
alpha = SDL_MapRGBA( newSurface->format, 0,0,0, color.unused);
SDL_Rect rect;
rect.x = 0;
rect.y = 0;
rect.h = fontsurf->h;
rect.w = fontsurf->w;
int ret = SDL_FillRect( newSurface, &rect, alpha);
SDL_SetAlpha(newSurface,SDL_SRCALPHA,SDL_ALPHA_TRANSPARENT);
ret = SDL_BlitSurface( fontsurf, 0, newSurface, 0 );
The opacity is applied correctly, but the SDL_MapRGBA creates a black background in the "newSurface".
How can I apply alpha/opacity to the text, without adding a background, only the text?
I think you are missing SDL_SetColorKey for newSurface.
Something like this:
SDL_SetColorKey( newSurface , SDL_SRCCOLORKEY, SDL_MapRGB( newSurface->format, 0, 0, 0) );
or even easier would be to skip filling newSurface with a black rectangle
remove this:
SDL_FillRect( newSurface, &rect, alpha);
Here is an example how I make a picture with transparent color:
testImage = IMG_Load( "trans.png" );
// This picture contains color 255,0,255 which will be set as transparent
SDL_SetColorKey( testImage , SDL_SRCCOLORKEY, SDL_MapRGB( screen->format, 255, 0, 255) );
// I set the color 255,0,255 as transparent for that surface
SDL_BlitSurface( testImage , NULL , screen , NULL );
// and I draw it on the main surface (screen)