Trouble getting 8-bit palette output working with SDL 2.0 - sdl

Here's my code, using SDL 2.0.4 on OSX 10.11.4:
SDL_Surface *output_surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
SDL_Texture *output_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_Color c[256];
// Setting each color to red as a test.
for(u8 i = 255; i--;) {
c[i].r = 255;
c[i].g = 0;
c[i].b = 0;
}
SDL_SetPaletteColors(output_surface->format->palette, c, 0, 256);
Then later...
SDL_Rect r = {
.x = 0,
.y = 0,
.w = width,
.h = height
};
// Doesn't fill with red.
SDL_FillRect(output_surface, &r, 4);
SDL_UpdateTexture(output_texture, NULL, output_surface->pixels, output_surface->pitch);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, output_texture, NULL, NULL);
SDL_RenderPresent(renderer);
What I would expect to see is the full window all red but I'm getting something entirely different. Changing the color number passed to SDL_FillRect shows that I'm getting a grayscale palette (0 is black, 255 is white) even though SDL_SetPaletteColors doesn't return an error and i've looped through output_surface->format->palette->colors to verify the palette's been changed.
What am I missing here?
edit: I was asked to post an entire program. Here it is:
int main(int argc, const char *argv[]) {
SDL_Window *window = NULL;
SDL_Renderer *renderer = NULL;
SDL_Surface *output_surface = NULL;
SDL_Texture *output_texture = NULL;
int width = 640;
int height = 480;
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) < 0) return 0;
window = SDL_CreateWindow("Sample", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, 0);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
output_surface = SDL_CreateRGBSurface(0, width, height, 8, 0, 0, 0, 0);
output_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_Color c[256];
for(u8 i = 255; i--;) {
c[i].r = 255;
c[i].g = 0;
c[i].b = 0;
c[i].a = 255;
}
SDL_SetPaletteColors(output_surface->format->palette, c, 0, 255);
SDL_Rect r = {
.x = 0,
.y = 0,
.w = width,
.h = height
};
bool running = true;
while(running) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
switch(event.type){
case SDL_KEYDOWN:
running = false;
break;
}
}
SDL_FillRect(output_surface, &r, 124);
SDL_UpdateTexture(output_texture, NULL, output_surface->pixels, output_surface->pitch);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, output_texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
SDL_FreeSurface(output_surface);
SDL_DestroyTexture(output_texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Passing 0 to SDL_FillRect is black, 255 is white, and any number in-between is a shade of grey.

Alright, found the solution.
Remove this line:
output_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_STREAMING, width, height);
And instead add this line somewhere after the call to SDL_SetPaletteColors or after you change the surfaces' pixels (like in the game loop):
output_texture = SDL_CreateTextureFromSurface(renderer, output_surface);

Related

MapRGB in sdl with cairo , the color is error

i use SDL2 with MapRGB to let win10 window transparent, and use cairo to draw something, but the color is not true, like that cairo use rgb(0.9, 0, 0) draw rect, SDL2 with SDL_MapRGB, and final the rect color is not red rgb.
#include <SDL.h>
#include <cairo.h>
#include <SDL_syswm.h>
#include "windows.h"
COLORREF defaultTransparentColor = RGB(255, 0, 255);
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* argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *window;
SDL_Renderer *renderer;
SDL_CreateWindowAndRenderer(640, 480, SDL_WINDOW_SHOWN, &window, &renderer);
windowColorKey(window, defaultTransparentColor);
SDL_SetWindowTitle(window, "Cairo test");
cairo_surface_t *surface;
cairo_t *cr;
#if 1
// 方式一 : 建立cairo, 建立texture, 在cairo上画图,然后update过去
int windowWidth = 640, windowHeight = 480;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, windowWidth, windowWidth);
cr = cairo_create(surface);
cairo_set_line_width(cr, 1);
// cairo use red color
cairo_set_source_rgb(cr, .9, 0, 0);
cairo_rectangle(cr, 100, 100, 180, 90);
cairo_stroke(cr);
unsigned char *data;
data = cairo_image_surface_get_data(surface);
SDL_Texture *texture;
SDL_Rect rect = {.x=0, .y=0, .w=windowWidth, .h=windowHeight};
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, windowWidth, windowHeight);
// SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_MUL);
int widthPitch = windowWidth << 2;
SDL_UpdateTexture(texture, &rect, data, widthPitch);
#else
// 方式二 : 建立texture, 从texture的像素数据建立cairo, 直接画到texture上去
// 可能比方式一快, 我没读过cairo_image_surface_create_for_data的源码,不敢妄下定论
SDL_Texture *texture;
SDL_Rect rect = {.x=0, .y=0, .w=100, .h=100};
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 100, 100);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
void *pixel;
int pitch = 400; // 一行的像素字节 ARGB32=4 byte, w = 100
SDL_LockTexture(texture, &rect, &pixel, &pitch);
surface = cairo_image_surface_create_for_data(
(unsigned char*) pixel,
CAIRO_FORMAT_ARGB32, 100, 100, pitch
);
SDL_UnlockTexture(texture);
cr = cairo_create(surface);
cairo_set_line_width(cr, 10);
cairo_set_source_rgb(cr, 255, 0, 0);
cairo_arc(cr, 50, 50, 20, 0.0, 0.785);
cairo_stroke(cr);
#endif
SDL_Event event;
bool is_working = true;
while (is_working) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
is_working = false;
break;
}
}
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
SDL_Delay(66);
}
return 0;
}
the rect is not red, what should i do ?

C++ SDL2 memcpy texture color initialization

In this question's answer we got this code to initialize a texture with some color:
int main(int argc, char* argv[])
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *MainWindow = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
1024, 768,
SDL_WINDOW_SHOWN
);
SDL_Renderer *renderer = SDL_CreateRenderer(MainWindow, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_Texture *Tile = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_STREAMING, 8, 8);
// Initialize texture pixels to a red opaque RGBA value
unsigned char* bytes = nullptr;
int pitch = 0;
SDL_LockTexture(Tile, nullptr, reinterpret_cast<void**>(&bytes), &pitch);
unsigned char rgba[4] = { 255, 0, 0, 255 };
for(int y = 0; y < 8; ++y) {
for (int x = 0; x < 8; ++x) {
memcpy(&bytes[(y * 8 + x)*sizeof(rgba)], rgba, sizeof(rgba));
}
}
SDL_UnlockTexture(Tile);
SDL_Rect destination = { 320, 240, 8, 8 };
SDL_RenderCopy(renderer, Tile, NULL, &destination);
SDL_RenderPresent(renderer);
SDL_Delay(3000);
//Clean up
SDL_DestroyTexture(Tile);
SDL_DestroyWindow(MainWindow);
SDL_Quit();
return 0;
}
It's supposed to initialize the texture with red, because rgba is initialized with {255, 0, 0, 255}. But I noticed that if I change this values, the result always shows some portion of red. Could someone explain this behavior? I guess I'm missing something in the memcpy line.
I would ask there, but I can't post comments.
Thanks! :)
EDIT: Here it is a capture with {0, 0, 255, 255}. With {0, 255, 255, 255} is all white, so it's like the red gets always added somehow.

How to use SDL_CreateTexture

I would like to know ho to use SDL_CreateTexture function.
I just want to create texture, give it a color and draw it somewhere on the screen. I dont wanna load any picture into it.
I thought I can use SDL_CreateTexture, SDL_SetTextureColorMod, SDL_RenderCopy, SDL_RenderPresent in that order, but I always get just a black rectangle instead of red one.
#include <SDL.h>
int main(int argc, char* argv[]) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *MainWindow = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
1024, 768,
SDL_WINDOW_SHOWN
);
SDL_Renderer *renderer = SDL_CreateRenderer(MainWindow, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_Texture *Tile = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,8,8);
//I want my texture to be red
SDL_SetTextureColorMod(Tile,255,0,0);
//I just try this i dont know if I have to do that
SDL_SetTextureAlphaMod(Tile,255);
SDL_Rect destination = {320,240,8,8};
SDL_RenderCopy(renderer,Tile,NULL,&destination);
SDL_RenderPresent(renderer);
SDL_Delay(3000);
//Clean up
SDL_DestroyTexture(Tile);
SDL_DestroyWindow(MainWindow);
SDL_Quit();
return 0;
}
I also dont know if I am using right format (SDL_PixelFormatEnum) value in SDL_CreateTexture. There is a lot of them and I dont understand what they mean. Which one can I use form this purpose?
SDL_SetTextureColorMod will make sure subsequent render copy operations will have the specified multiplier taken into account. It doesn't change color to the texture texels.
You should rather either load a bitmap or initialize your texture with a red color as in the following example
int main(int argc, char* argv[]) {
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Window *MainWindow = SDL_CreateWindow("My Game Window",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
1024, 768,
SDL_WINDOW_SHOWN
);
SDL_Renderer *renderer = SDL_CreateRenderer(MainWindow, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderClear(renderer);
SDL_Texture *Tile = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_STREAMING, 8, 8);
// Initialize texture pixels to a red opaque RGBA value
unsigned char* bytes = nullptr;
int pitch = 0;
SDL_LockTexture(Tile, nullptr, reinterpret_cast<void**>(&bytes), &pitch);
unsigned char rgba[4] = { 255, 0, 0, 255 };
for(int y = 0; y < 8; ++y) {
for (int x = 0; x < 8; ++x) {
memcpy(&bytes[(y * 8 + x)*sizeof(rgba)], rgba, sizeof(rgba));
}
}
SDL_UnlockTexture(Tile);
SDL_Rect destination = { 320, 240, 8, 8 };
SDL_RenderCopy(renderer, Tile, NULL, &destination);
SDL_RenderPresent(renderer);
SDL_Delay(3000);
//Clean up
SDL_DestroyTexture(Tile);
SDL_DestroyWindow(MainWindow);
SDL_Quit();
return 0;
}

SDL2: Generate fully transparent texture

How using SDL_CreateTexture create transparent texture? By default I'm creating texure with such code:
SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET, x, y);
And then I'm paining on this texture with redirecting output to this texture. However at the end what I want to render this on screen any (nonupdated) pixel is black.
I have tried different ways with using of:
SDL_RenderClear(_Renderer);
or even with drawing and on created texture with painting transparent Rect with different blending modes but all I had as a result was still nontransparent texture :/
SDL_Rect rect={0,0,Width,Height};
SDL_SetRenderDrawBlendMode(_Renderer,SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(_Renderer,255,255,255,0);
SDL_RenderFillRect(_Renderer,&rect);
To be more specific:
//this->texDefault.get()->get() = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET, x, y);
SDL_SetRenderTarget(_Renderer.get()->get(), this->texDefault.get()->get());
SDL_SetRenderDrawBlendMode(this->_Renderer.get()->get(),SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(this->_Renderer.get()->get(),255,0,255,0);
SDL_RenderClear(this->_Renderer.get()->get());
//SDL_Rect rect={0,0,Width,Height};
//SDL_SetRenderDrawColor(this->_Renderer.get()->get(),255,255,255,255);
//SDL_RenderFillRect(this->_Renderer.get()->get(),&rect);
//SDL_RenderClear(this->_Renderer.get()->get());
//SDL_SetRenderDrawBlendMode(this->_Renderer.get()->get(),SDL_BLENDMODE_NONE);
SDL_SetRenderTarget(_Renderer.get()->get(), NULL);
SDL_Rect rect= {relTop+Top,relLeft+Left,Height,Width};
SDL_SetRenderDrawBlendMode(this->_Renderer.get()->get(),SDL_BLENDMODE_BLEND);
SDL_RenderCopy(this->_Renderer.get()->get(), this->texDefault->get(), NULL, &rect);
This code is always producing nontransparent Texture independenty what i will set for blending and alpha
The result is :
Maybe there is some other simple method to create transparent empty texture in SDL2 something like x/y-sized fully transparent png but loading having such image in file is little bit pointless :/
First, you need to set renderer blend mode: SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);.
Second, you need to set texture blend mode: SDL_SetTextureBlendMode(textures[i], SDL_BLENDMODE_BLEND);.
Here is working example I created. You can use keys A and S to change alpha channel of third texture, which is invisible at start of the application.
#include <iostream>
#include <vector>
#include <SDL.h>
void fillTexture(SDL_Renderer *renderer, SDL_Texture *texture, int r, int g, int b, int a)
{
SDL_SetRenderTarget(renderer, texture);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
SDL_SetRenderDrawColor(renderer, r, g, b, a);
SDL_RenderFillRect(renderer, NULL);
}
void prepareForRendering(SDL_Renderer *renderer)
{
SDL_SetRenderTarget(renderer, NULL);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
}
void checkSdlError()
{
const char *sdlError = SDL_GetError();
if(sdlError && *sdlError)
{
::std::cout << "SDL ERROR: " << sdlError << ::std::endl;
}
}
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_HAPTIC);
SDL_Window *window = SDL_CreateWindow("SDL test",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
320, 240,
SDL_WINDOW_OPENGL);
SDL_Renderer *renderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
const int width = 50;
const int height = 50;
::std::vector<SDL_Texture*> textures;
SDL_Texture *redTexture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
textures.push_back(redTexture);
SDL_Texture *greenTexture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
textures.push_back(greenTexture);
SDL_Texture *purpleTexture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height);
textures.push_back(purpleTexture);
// Here is setting the blend mode for each and every used texture:
for(int i = 0; i < textures.size(); ++i)
{
SDL_SetTextureBlendMode(textures[i], SDL_BLENDMODE_BLEND);
}
int purpleAlpha = 0;
fillTexture(renderer, redTexture, 255, 0, 0, 255);
fillTexture(renderer, greenTexture, 0, 255, 0, 128);
fillTexture(renderer, purpleTexture, 255, 0, 255, purpleAlpha);
prepareForRendering(renderer);
bool running = true;
while(running)
{
SDL_Rect rect;
rect.w = width;
rect.h = height;
SDL_RenderClear(renderer);
rect.x = 50;
rect.y = 50;
SDL_RenderCopy(renderer, redTexture, NULL, &rect);
rect.x = 75;
rect.y = 70;
SDL_RenderCopy(renderer, greenTexture, NULL, &rect);
rect.x = 75;
rect.y = 30;
SDL_RenderCopy(renderer, purpleTexture, NULL, &rect);
SDL_RenderPresent(renderer);
// Process events
{
SDL_Event event;
while(SDL_PollEvent(&event) == 1)
{
if(event.type == SDL_QUIT)
{
running = false;
}
else if(event.type == SDL_KEYDOWN)
{
switch(event.key.keysym.sym)
{
case SDLK_ESCAPE:
running = false;
break;
case SDLK_a:
purpleAlpha = ::std::max(purpleAlpha - 32, 0);
fillTexture(renderer, purpleTexture, 255, 0, 255, purpleAlpha);
prepareForRendering(renderer);
::std::cout << "Alpha: " << purpleAlpha << ::std::endl;
break;
case SDLK_s:
purpleAlpha = ::std::min(purpleAlpha + 32, 255);
fillTexture(renderer, purpleTexture, 255, 0, 255, purpleAlpha);
prepareForRendering(renderer);
::std::cout << "Alpha: " << purpleAlpha << ::std::endl;
break;
}
}
}
checkSdlError();
}
}
for(int i = 0; i < textures.size(); ++i)
{
SDL_DestroyTexture(textures[i]);
}
textures.clear();
SDL_DestroyRenderer(renderer);
renderer = NULL;
SDL_DestroyWindow(window);
window = NULL;
SDL_Quit();
checkSdlError();
return 0;
}
EDIT: Completely rewritten the answer, original one basically contained blend mode of renderer.

Can't see my text using SDL TTF for SDL 2

I guess I do all right, but anyway I don't see my text. Official docs are poor (or obsolete for SDL 2).
if (TTF_Init())
printf("%s\n", TTF_GetError());
font = TTF_OpenFont("Fonts/DinCompRegular.ttf", 24);
if (!font)
printf("%s\n", TTF_GetError());
SDL_Color fontColor = { 255, 0, 0, 255 };
u16 text[] = { '2', 's', 'ä', 'Щ' };
SDL_Surface *info = TTF_RenderUNICODE_Solid(font, text, fontColor);
SDL_Rect rect = { info->w, info->h, 0, 0 };
SDL_Surface *place = SDL_CreateRGBSurface(0, info->w, info->h, 32, 0, 0, 0, 0);
And in main loop:
SDL_BlitSurface(info, 0, place, &rect);
SDL_FreeSurface(info);
SDL_RenderPresent(renderer);
I solved it by myself:
u16 text[] = { '2', L'Щ', L'ä', L'Ø', '\0' };
SDL_Surface *info = TTF_RenderUNICODE_Blended(font, text, fontColor);
Loop:
SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, info);
SDL_Rect src = { 0, 0, info->w, info->h };
SDL_Rect dst = { 0, 0, info->w, info->h };
SDL_RenderCopy(renderer, texture, &src, &dst);
SDL_DestroyTexture(texture);