Display transparent images with black strokes

Black borders appear after displaying(About 1px)


This is part of the code:

SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, &dstRect);
SDL_RenderPresent(renderer);

LONG_PTR style = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
SetWindowLongPtr(hwnd, GWL_EXSTYLE, style | WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TRANSPARENT);
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

Welcome vlad.

By default, transparent pixels in a texture aren’t transparent. Try this:

SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);

Thank you very much for your answer, but it seems it will still exist. Below is all my code:
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_syswm.h>
#include <stdio.h>
#include <windows.h>
#include <shellapi.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>

// Constant Definitions
#define IMAGE_DIR “./cat” // Image folder directory
#define SWITCH_INTERVAL 50 // Image switch interval (in milliseconds)

// Check if the file is in PNG format
int is_png(const char *filename) {
const char *ext = strrchr(filename, ‘.’);
return ext != NULL && strcmp(ext, “.png”) == 0;
}

// Get all PNG files in the specified directory
int get_png_files(const char *dir, char ***image_files) {
DIR *d = opendir(dir);
if (d == NULL) {
fprintf(stderr, “Failed to open directory: %s\n”, dir);
return 0;
}

struct dirent *entry;
int count = 0;
while ((entry = readdir(d)) != NULL) {
    if (is_png(entry->d_name)) {
        (*image_files) = realloc(*image_files, sizeof(char*) * (count + 1));
        (*image_files)[count] = malloc(strlen(dir) + strlen(entry->d_name) + 2);
        sprintf((*image_files)[count], "%s/%s", dir, entry->d_name);
        count++;
    }
}
closedir(d);
return count;

}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, “SDL_Init Error: %s\n”, SDL_GetError());
return 1;
}

// Initialize SDL_image
if (IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG == 0) {
    fprintf(stderr, "IMG_Init Error: %s\n", IMG_GetError());
    SDL_Quit();
    return 1;
}

// Get all PNG files in IMAGE_DIR directory
char **image_files = NULL;
int image_count = get_png_files(IMAGE_DIR, &image_files);
if (image_count == 0) {
    fprintf(stderr, "No PNG files found in %s\n", IMAGE_DIR);
    IMG_Quit();
    SDL_Quit();
    return 1;
}

// Get the width and height of the first image
SDL_Surface *image = IMG_Load(image_files[0]);
if (image == NULL) {
    fprintf(stderr, "IMG_Load Error: %s\n", IMG_GetError());
    IMG_Quit();
    SDL_Quit();
    return 1;
}

int imgWidth = image->w;
int imgHeight = image->h;

// Create window, adjust window size according to image size
SDL_Window *window = SDL_CreateWindow("SDL2 Image Display", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, imgWidth, imgHeight, SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS);
if (window == NULL) {
    fprintf(stderr, "SDL_CreateWindow Error: %s\n", SDL_GetError());
    SDL_FreeSurface(image);
    IMG_Quit();
    SDL_Quit();
    return 1;
}

// Get the native window handle
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
if (SDL_GetWindowWMInfo(window, &wmInfo) != 1) {
    fprintf(stderr, "SDL_GetWindowWMInfo failed\n");
    SDL_DestroyWindow(window);
    SDL_FreeSurface(image);
    IMG_Quit();
    SDL_Quit();
    return 1;
}

HWND hwnd = wmInfo.info.win.window;

// Set the window as a tool window
LONG_PTR style = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
SetWindowLongPtr(hwnd, GWL_EXSTYLE, style | WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TRANSPARENT);

// Set the window to be transparent and allow mouse penetration
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 0, LWA_COLORKEY); // Set the transparent color key to black

// Set the window to always be on top
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

// Create renderer
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer == NULL) {
    fprintf(stderr, "SDL_CreateRenderer Error: %s\n", SDL_GetError());
    SDL_DestroyWindow(window);
    SDL_FreeSurface(image);
    IMG_Quit();
    SDL_Quit();
    return 1;
}

// Create texture
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, image);
SDL_FreeSurface(image); // No longer needed
if (texture == NULL) {
    fprintf(stderr, "SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError());
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    IMG_Quit();
    SDL_Quit();
    return 1;
}

// Set blend mode
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); // Set texture blend mode to blend

// Set tray icon
NOTIFYICONDATA nid;
ZeroMemory(&nid, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.uID = 1;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
nid.uCallbackMessage = WM_APP; // Set message callback
nid.hIcon = (HICON)LoadImage(NULL, "icon.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE); // Icon path

// Modify nid.szTip type to wchar_t array
wchar_t szTip[128];  // Define as wchar_t type
wcsncpy(szTip, L"My Tray Icon", sizeof(szTip) / sizeof(wchar_t)); // Wide-character string
wcscpy((wchar_t*)nid.szTip, szTip); // Fill with wide-character string

Shell_NotifyIcon(NIM_ADD, &nid);

// Event loop
SDL_Event e;
int quit = 0;
int current_image_index = 0;
Uint32 last_time = SDL_GetTicks();

while (!quit) {
    while (SDL_PollEvent(&e)) {
        if (e.type == SDL_QUIT) {
            quit = 1;
        }
    }

    // Switch the image every SWITCH_INTERVAL milliseconds
    Uint32 current_time = SDL_GetTicks();
    if (current_time - last_time >= SWITCH_INTERVAL) {
        last_time = current_time;
        current_image_index = (current_image_index + 1) % image_count;

        // Load the next image
        SDL_Surface *new_image = IMG_Load(image_files[current_image_index]);
        if (new_image == NULL) {
            fprintf(stderr, "IMG_Load Error: %s\n", IMG_GetError());
            continue;
        }

        // Create new texture
        SDL_Texture *new_texture = SDL_CreateTextureFromSurface(renderer, new_image);
        SDL_FreeSurface(new_image);

        if (new_texture == NULL) {
            fprintf(stderr, "SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError());
            continue;
        }

        SDL_DestroyTexture(texture);  // Destroy old texture
        texture = new_texture;        // Update to the new texture
    }

    // Render the image
    SDL_RenderClear(renderer);
    SDL_Rect dstRect = {0, 0, imgWidth, imgHeight}; // Target rectangle, specifying the image size
    SDL_RenderCopy(renderer, texture, NULL, &dstRect); // Render using the specified size
    SDL_RenderPresent(renderer);

    SDL_Delay(10); // Prevent excessive CPU usage
}

// Clean up resources
Shell_NotifyIcon(NIM_DELETE, &nid); // Remove tray icon
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
IMG_Quit();
SDL_Quit();

// Free image filenames array
for (int i = 0; i < image_count; i++) {
    free(image_files[i]);
}
free(image_files);

return 0;

}

This is more Windows thing. As I can see from the code, you want to display an image with an alpha channel on a translucent window. With the current approach (color key) you can only get either non-transparent pixels or fully transparent pixels, hence the black border - alpha of these pixels from source image is not 0 or 255, so black color key will not work (because they aren’t black after blending).

Thank you very very very much for your advice, I give it a try