I am trying to create an SDL window which is by itself fully transparent, but when an image is rendered onto it, the image is fully opaque and the alpha channel of the image is conserved (so the transparent pixels of the image remain transparent). So the result would be just an image on the screen, with no container around it (Imagine sticking a real-life sticker on your screen)
So here is the simplified code for that:
#include <stdio.h>
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_syswm.h>
#include <iostream>
#include <Windows.h>
#include <string>
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Texture* image;
int imageWidth = 0;
int imageHeight = 0;
bool init()
{
//Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
return false;
}
//Set texture filtering to linear
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"))
{
printf("Warning: Linear texture filtering not enabled!");
}
//Create window
window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 500, 300, SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS);
if (window == NULL)
{
printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
return false;
}
//Create renderer for window
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer == NULL)
{
printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError());
return false;
}
//Initialize renderer color
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
//Initialize PNG loading
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
return false;
}
return true;
}
bool loadImage(const std::string& path)
{
//The final texture
SDL_Texture* newImage = NULL;
//Load image at specified path
SDL_Surface* loadedSurface = IMG_Load(path.c_str());
if (!loadedSurface)
{
printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError());
return false;
}
//Create texture from surface pixels
newImage = SDL_CreateTextureFromSurface(renderer, loadedSurface);
if (!newImage)
{
printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
return false;
}
imageWidth = loadedSurface->w;
imageHeight = loadedSurface->h;
//Get rid of old loaded surface
SDL_FreeSurface(loadedSurface);
image = newImage;
return true;
}
void displayImage()
{
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
SDL_RenderClear(renderer);
SDL_SetWindowSize(window, imageWidth, imageHeight);
SDL_Rect renderQuad = { 0, 0, imageWidth, imageHeight };
SDL_RenderCopy(renderer, image, NULL, &renderQuad);
SDL_RenderPresent(renderer);
}
// Makes a window transparent by setting a transparency color.
bool windowColorKey(SDL_Window* window, COLORREF colorKey) {
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version); // Initialize wmInfo
SDL_GetWindowWMInfo(window, &wmInfo);
HWND hWnd = wmInfo.info.win.window;
// Change window type to layered
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
// Set transparency color
return SetLayeredWindowAttributes(hWnd, colorKey, 0, LWA_COLORKEY);
}
int main(int argc, char* args[]) {
// Initialize SDL: Create a window and renderer
init();
// Load the image
loadImage("example.png");
// Color key the background of the window (magenta)
windowColorKey(window, RGB(255, 0, 255));
// Render the image on the widnow with magenta for backround color
displayImage();
// Check for events
bool windowActive = true;
SDL_Event event;
while (windowActive)
{
while (SDL_PollEvent(&event) != 0)
{
if (event.type == SDL_QUIT || event.key.keysym.scancode == SDL_SCANCODE_ESCAPE) { windowActive = false; }
}
}
return 0;
}
Using SDL_SetWindowOpacity() won't work because all the elements of the window transparent too.
For now the only way I know to achieve this goal is using color keying, but unfortunately it doesn't work that well, since some of the images will contain the color you use for color keying, and additionally the images will have a small border of the color you used for color keying all around the image. It's thin but annoying.
I am thinking of using the features of a layered window in some other way but cannot find or understand how to.
So, the question is how to make the window itself fully transparent, while having full control over the transparency of the elements of the window?
Related
I'm having some trouble understanding texture streaming and loading a 2D texture from an array of raw pixel data.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <SDL2/SDL.h>
#define WINDOW_W 640
#define WINDOW_H 320
int main(int argc, char *argv[])
{
if (SDL_Init(SDL_INIT_EVERYTHING))
exit(1);
/* Set up main window. */
SDL_Window *window = SDL_CreateWindow(
"Texture Streaming",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
WINDOW_W,
WINDOW_H,
SDL_WINDOW_SHOWN
);
if (window == NULL)
exit(2);
/* Set up renderer. */
SDL_Renderer *renderer = SDL_CreateRenderer(
window,
-1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
);
if (renderer == NULL)
exit(3);
SDL_Event ev;
int window_running = 1;
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, 64, 32);
Uint32 pixels[64 * 32];
for (int i = 0; i < (64 * 32); i++)
pixels[i] = 0xFF00FFFF;
for (int i = 0; i < 64; i++)
pixels[i] = 0xFF0000FF;
SDL_UpdateTexture(texture, NULL, pixels, 4);
while (window_running)
{
while (SDL_PollEvent(&ev) != 0)
{
if (ev.type == SDL_QUIT)
window_running = 0;
}
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Instead of my program drawing the first 64 pixels red and the rest magenta, it just spits out a random chunk of red and yellow pixels.
I'm having trouble understanding SDL_CreateTexture and SDL_UpdateTexture.
Your pitch in UpdateTexture is wrong. Pitch is byte length of a row, not single pixel. In your case pitch is 64*4.
Pixel format SDL_PIXELFORMAT_RGBA32 is alias to either SDL_PIXELFORMAT_RGBA8888 or SDL_PIXELFORMAT_ABGR8888 (latter in your case). Look at https://wiki.libsdl.org/SDL_PixelFormatEnum . So your 0xFF00FFFF is yellow (R=0xff, G=0xff, B=0, A=0xff).
Finally, if we're talking about texture streaming (i.e. updating every frame or otherwise very often), you should create streaming texture and use LockTexture/UnlockTexture. UpdateTexture is for static textures.
I need to build an interface where on the left side of the screen shows part of one streaming video and the right side the other part. Something like this https://www.youtube.com/watch?v=fSPXpdVzamo
The video streaming is saved on a memory buffer that is being loaded on a texture. My question is how to render just the half of the texture, I've bee trying using SDL_Rect but nothing happens.
This is the relevant part of my code:
SDL_UpdateTexture(texture, NULL, buffer_start, fmt.fmt.pix.width * 2);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
If I try something like this, it doesn't work:
SDL_UpdateTexture(texture, NULL, buffer_start, fmt.fmt.pix.width * 2);
SDL_Rect someRect;
someRect.x = 0;
someRect.y = 0;
someRect.w = 1500;
someRect.h = 3000;
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, &someRect);
SDL_RenderPresent(renderer);
Any advice would be great!
Without you posting a MCVE is hard to know where you went wrong. My guess is your x position is wrong. Here is an example where I show how to draw 2 images in the fashion of your video.
Green image: https://i.imgur.com/yaOG8Ng.png
Red image: https://i.imgur.com/faKKShU.png
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#define HEIGHT 600
#define WIDTH 800
using namespace std;
int main() {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window = SDL_CreateWindow("Red Green", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN);
SDL_Renderer *renderer = SDL_CreateRenderer(window, 0, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
bool quit = false;
SDL_Event event;
SDL_Texture *green_part = IMG_LoadTexture(renderer, "Green400x600.png");
SDL_Texture *red_part = IMG_LoadTexture(renderer, "Red400x600.png");
while (!quit) {
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
quit = true;
}
}
SDL_RenderClear(renderer);
SDL_Rect copy_rect{0, 0, 400, 600};
SDL_RenderCopy(renderer, green_part, nullptr, ©_rect);
// We now draw from half the screen onward x position = WIDTH / 2.
copy_rect.x = 400;
SDL_RenderCopy(renderer, red_part, nullptr, ©_rect);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
return 0;
}
I have been working on a fairly large app in SDL for a while and recently noticed a strange bug; whenever a window is closed, everything on another window will render completely black. When I draw red a line, it will be black, and when I draw an SDL_Texture, it will draw a black rectangle in place of the image.
After a while, I managed to recreate the problem by making a simplified version of the app from the ground up. The program includes a window class that stores a window, its renderer, and an SDL_Texture. The window class also includes a render function to render things on to its window including the SDL_Texture. There is also a function that allows each window object to handle SDL events such as the window being closed. When the window is closed, the window, renderer, and texture are all destroyed.
class Window {
SDL_Window *window;
SDL_Renderer *renderer;
Uint32 windowID;
SDL_Texture *texture;
void cleanup() {
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_DestroyTexture(texture);
}
bool running = true;
public:
void init() {
window = SDL_CreateWindow("Window", 50, 50, 721, 558, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
windowID = SDL_GetWindowID(window);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
// load texture
SDL_Surface* loadedSurface = IMG_Load("picture.png");
texture = SDL_CreateTextureFromSurface(renderer, loadedSurface);
SDL_FreeSurface(loadedSurface);
}
bool isRunning() {
return running;
}
void render() {
// clear the screen with a green color
SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
SDL_RenderClear(renderer);
// create a rectangle for drawing things
SDL_Rect rect = {0, 0, 100, 100};
// draw a red rectangle
SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0xFF);
SDL_RenderFillRect(renderer, &rect);
// draw the texture/image on top of the rectangle
if (SDL_RenderCopy(renderer, texture, NULL, &rect) < 0) {
printf("Unable to render texture! Error: %s\n", SDL_GetError());
}
SDL_RenderPresent(renderer);
}
void events(SDL_Event &e) {
if (e.window.windowID == windowID && e.window.event == SDL_WINDOWEVENT_CLOSE) {
running = false;
cleanup();
}
}
};
The main function manages multiple windows at a time, and separately feeds each one of them SDL events:
int main(int argc, const char * argv[]) {
if (SDL_Init(SDL_INIT_VIDEO) < 0) { // initialize SDL and IMG
printf("SDL could not initialize! Error: %s\n", SDL_GetError());
} else if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG)) {
printf("SDL_image could not initialize! Error: %s\n", IMG_GetError());
} else {
std::vector<Window*> windows; // vector of window objects
// add window objects to the vector
windows.push_back(new Window());
windows.push_back(new Window());
windows.push_back(new Window());
windows.push_back(new Window());
// initialize all windows
for (int i = 0; i < windows.size(); i++) {
windows[i]->init();
}
// game loop
bool loop = true;
while (loop) {
SDL_Delay(50); // delay between each frame
// render all windows
for (int i = 0; i < windows.size(); i++) {
windows[i]->render();
}
// handle new events
SDL_Event e;
while (SDL_PollEvent(&e)) {
// loop backward through windows
// in case one of them has to be
// removed from the vector
for (unsigned long i = windows.size(); i-- > 0;) {
if (windows[i]->isRunning()) {
windows[i]->events(e);
} else {
// delete a window if it has been closed
delete windows[i];
windows.erase(windows.begin() + i);
}
}
}
if (windows.empty()) { // if all windows are closed,
loop = false; // stop the loop
}
}
}
return 0;
}
Now that I have shown my code, I will explain the issue in more detail. When the program starts, four window objects are created, and the windows each show up on the screen. Each of the windows render properly, with a PNG image overlaid on top of a red rectangle. The fourth window, or the last one to be initialized, will only render things such as images, rectangles, and lines in the color black after any other window is closed. The only thing that will render in a different color is SDL_RenderClear, which I used in the above code to render a green background, which exposes the black image and rectangle shapes.
I have tested to make sure the texture, renderer, and window are still valid after this problem occurs, and got no errors.
I would at least like to find what is causing the problem and if there is any possible solution.
You should not destroy window while renderer is still alive. Try executing methods inside of cleanup() in opposite order.
void cleanup()
{
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
}
This is just a training excersize to get myself working with SDL, ignore the lack of functions, warnings and that everything is in main. I keep getting a segmentation fault, it is certainly what i've done with the section under the //Initialise TTF, create font pointer, set font and colour then render. section, as the code without that works fine.
#include <SDL2/SDL.h>
#include <SDL/SDL_ttf.h>
#include <stdio.h>
const int SCREEN_WIDTH = 1000;
const int SCREEN_HEIGHT = 1000;
int main(int argc, char* args[])
{
// Set up window, surface, image and font pointers
SDL_Window* gWindow = NULL;
SDL_Surface* gScreenSurface = NULL;
SDL_Surface* gImage1 = NULL;
SDL_Surface* gImage2 = NULL;
SDL_Surface* text = NULL;
// Get a window surface, load images
gWindow = SDL_CreateWindow( "Image in a window, and some text", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
gScreenSurface = SDL_GetWindowSurface(gWindow);
gImage1 = SDL_GetWindowSurface(gWindow);
gImage2 = SDL_GetWindowSurface(gWindow);
gImage1 = SDL_LoadBMP("./Image1.bmp");
gImage2 = SDL_LoadBMP("./Image2.bmp");
------------------------------------------------------
//Initialise TTF, create font pointer, set font and colour then render.
TTF_Init();
TTF_Font* font;
font = TTF_OpenFont("./LucidaBrightDemiBold.ttf", 24);
SDL_Color text_color = {255, 255, 255, 255};
text = TTF_RenderText_Solid(font, "Using TTF to write stuff", text_color);
-------------------------------------------------------
// Apply the image, update the surface with the image
SDL_BlitSurface(gImage1, NULL, gScreenSurface, NULL);
SDL_BlitSurface(gImage2, NULL, gScreenSurface, NULL);
SDL_BlitSurface(text, NULL, gScreenSurface, NULL);
SDL_UpdateWindowSurface( gWindow );
SDL_Delay(5000);
// Free surfaces
SDL_FreeSurface(gImage1);
SDL_FreeSurface(gImage2);
SDL_FreeSurface(text);
// Nullify surfaces
gImage1 = NULL;
gImage2 = NULL;
text = NULL;
//Close and nullify window
SDL_DestroyWindow(gWindow);
gWindow = NULL;
//Quit images
TTF_Quit();
SDL_Quit();
return 0;
}
I think the problem lies between the -------'s.
I'm trying to create a transparent sprite with SDL. I'm using SDL_SetColorKey on a bitmap with magenta (0xff00ff) background (it's 100% magenta, I checked it with the GIMP :)) The call to SDL_SetColorKey looks like this:
SDL_SetColorKey( bitmap, SDL_SRCCOLORKEY, SDL_MapRGB(bitmap->format, 255, 0, 255) );
The call to SDL_SetColorKey apparently returns 0, however there is no transparency. Can anyone tell me what I am missing here?
Here is the problematic code in case anyone wants to test it:
#include "SDL/SDL.h"
const int WINDOW_WIDTH = 640;
const int WINDOW_HEIGHT = 480;
const char* WINDOW_TITLE = "SDL Start";
int main(int argc, char **argv)
{
SDL_Init( SDL_INIT_VIDEO );
SDL_Surface* screen = SDL_SetVideoMode( WINDOW_WIDTH, WINDOW_HEIGHT, 0,
SDL_HWSURFACE | SDL_DOUBLEBUF );
SDL_WM_SetCaption( WINDOW_TITLE, 0 );
SDL_Surface* bitmap = SDL_LoadBMP("resources/ship.bmp");
if(SDL_SetColorKey( bitmap, SDL_SRCCOLORKEY, SDL_MapRGB(bitmap->format, 255, 0, 255) )) printf("aaaaa %s", SDL_GetError());
// Part of the screen we want to draw the sprite to
SDL_Rect destination;
destination.x = 100;
destination.y = 100;
destination.w = 65;
destination.h = 44;
SDL_Event event;
bool gameRunning = true;
while (gameRunning)
{
if (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
gameRunning = false;
}
}
SDL_BlitSurface(bitmap, NULL, screen, &destination);
SDL_Flip(screen);
}
SDL_FreeSurface(bitmap);
SDL_Quit();
return 0;
}
UPDATE:
In case anyone needs it, here is the bitmap: http://dl.dropbox.com/u/8936880/ship.bmp
The problem is with your image, i used one generated by me and it works out of the box with your code.
Your image is in 32 bits and it seems that SDL_SetColorKey doesn't like it, convert it to 24 bits and it should work.
You can convert it with Gimp when you save it to BMP from the advanced settings.
Try with this one converted to 24 bits.