How to render a rectangle SDL2 texture from a buffer of hex values? - c++

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.

Related

SDL: How to set size of pixel (stretch) of a window

I'm making a retro pixel game so I want to make my window a very low resolution (256x256). However when I try to make it fullscreen, the whole window was just rendered top left, while leaving all other areas black.
I want to know a way of globally setting the size of each pixel in a window, in order to let it fit the fullscreen, or, a way of stretching the whole window (or a renderer?) to a specified size(and full screen) while having the (w, h) unchanged in parameter 2 and 3 in SDL_CreateWindow, and also whilst having the sizes proportional (so, if it was a square window, it should be a square window after stretched, not a rect after stretched into a rect displayer).
First, render your game to a 256x256 texture. This gist has an example, I will inline it below.
Next, figure out the correct size and position of your game texture on your actual window, and render the texture there. That will require modifications to the SDL_RenderCopyEx call, as the gist simply renders it stretched to the screen.
#include <iostream>
#ifdef __linux__
#include <SDL2/SDL.h>
#elif defined(_WIN32)
#include <SDL.h>
#endif
const int WIN_WIDTH = 640;
const int WIN_HEIGHT = 480;
int main(int argc, char **argv){
if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
std::cerr << "SDL_Init failed: " << SDL_GetError() << "\n";
return 1;
}
SDL_Window *win = SDL_CreateWindow("Rendering to a texture!", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, WIN_WIDTH, WIN_HEIGHT, 0);
SDL_Renderer *renderer = SDL_CreateRenderer(win, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
//Put your own bmp image here
SDL_Surface *bmpSurf = SDL_LoadBMP("../res/image.bmp");
SDL_Texture *bmpTex = SDL_CreateTextureFromSurface(renderer, bmpSurf);
SDL_FreeSurface(bmpSurf);
//Make a target texture to render too
SDL_Texture *texTarget = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_TARGET, WIN_WIDTH, WIN_HEIGHT);
//Now render to the texture
SDL_SetRenderTarget(renderer, texTarget);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, bmpTex, NULL, NULL);
//Detach the texture
SDL_SetRenderTarget(renderer, NULL);
//Now render the texture target to our screen, but upside down
SDL_RenderClear(renderer);
SDL_RenderCopyEx(renderer, texTarget, NULL, NULL, 0, NULL, SDL_FLIP_VERTICAL);
SDL_RenderPresent(renderer);
SDL_Delay(1000);
SDL_DestroyTexture(texTarget);
SDL_DestroyTexture(bmpTex);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}

Showing a video using two textures SDL2

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, &copy_rect);
// We now draw from half the screen onward x position = WIDTH / 2.
copy_rect.x = 400;
SDL_RenderCopy(renderer, red_part, nullptr, &copy_rect);
SDL_RenderPresent(renderer);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
return 0;
}

Confused on SDL_CreateRGBSurface() width and height parameters

So, after fooling around with the width and height numbers on the SDL_CreateRGBSurface() function, i am really confused on how they work. According to SDL Wiki the width and height refer to the width and height of the surface, however when I say SCREENWIDTH / 2, or SCREENHEIGHT / 2, it shows bigger than without the division. This is the code I was messing around with:
#include <iostream>
#include <SDL.h>
const int WIN_WIDTH = 640;
const int WIN_HEIGHT = 480;
int main(int argc, char **argv){
if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
std::cerr << "SDL_Init failed: " << SDL_GetError() << "\n";
return 1;
}
SDL_Window *win = SDL_CreateWindow("Rendering to a texture!", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, WIN_WIDTH, WIN_HEIGHT, 0);
SDL_Renderer *renderer = SDL_CreateRenderer(win, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE);
SDL_Surface* s;
SDL_Color c = { 155, 0, 0 };
SDL_Rect r = { 0, 0, 100, 100 };
s = SDL_CreateRGBSurface(0, WIN_WIDTH / 2, WIN_HEIGHT, 32, 0, 0, 0, 0);
SDL_FillRect(s, &r, SDL_MapRGB(s->format, c.r, c.g, c.b));
SDL_Texture* t;
t = SDL_CreateTextureFromSurface(renderer, s);
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, t, NULL, NULL);
SDL_RenderPresent(renderer);
SDL_Delay(2000);
SDL_FreeSurface(s);
SDL_DestroyTexture(t);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
All I really want is a rect with a width of 100 and height of 100, but to have it appear correctly on the screen, the width and height must be specified to the width and height of the window. Why is that?
SDL_RenderCopy will stretch the source texture to fit into the destination renderer. So it will stretch your surface over the entire window. If you want to draw a 100x100 pixel rectangle, your SDL surface should be the same size as the window you're copying it to; that way no stretching will take place and the surface will be presented 1:1.

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;
}

Problems with SDL_SetColorKey

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.