I am trying to render a custom image and it requires that I load the file into memory and render it out through SDL. The image is a raw format and I think if I could render
My code might be garbage , so I am open to changes with it.
void Create_SDL_Window()
{
SDL_Init(SDL_INIT_EVERYTHING);
IMG_Init(IMG_INIT_PNG);
window = SDL_CreateWindow("Test Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, 0);
printf("Window And Renderer Created!\n");
}
int main(){
FILE* customImage = fopen(Path, "rb");
Create_SDL_Window();
while (!quit){
void *p;
p = customImage;
SDL_Texture* buffer = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA8888,SDL_TEXTUREACCESS_STREAMING, 800, 600);
SDL_LockTexture(buffer, NULL, &p, &pitch);
SDL_UnlockTexture(buffer);
SDL_RenderCopy(renderer, buffer, NULL, NULL);
SDL_RenderPresent(renderer);
while (SDL_PollEvent(&e)){
//If user closes the window
if (e.type == SDL_QUIT){
quit = true;
}
//If user presses any key
if (e.type == SDL_KEYDOWN){
// quit = true;
}
//If user clicks the mouse
if (e.type == SDL_MOUSEBUTTONDOWN){
/// quit = true;
}
}
SDL_RenderPresent(renderer);
}
You have things backwards. You should notice that SDL_LockTexture takes a pointer-to-a-pointer. This is because SDL already has a buffer appropriately sized for the texture and it needs to tell you the address (and pitch) so that you can write to this buffer.
You also have the problem that you think you can use a FILE* as a pixel buffer. This is not true at all; FILE* is a pointer to a structure describing the file, not its contents.
What you need to do is something like:
// create your empty texture
...
int pitch = 0;
char* p = NULL;
SDL_LockTexture(buffer, NULL, &p, &pitch);
... // Error checking
// now open your file and mmap it
int fd = open(Path, O_RDONLY);
struct stat sb;
fstat(fd, &sb);
const char* memblock = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
... // Error checking
// now you need to copy the data from the file to the pixel buffer
// you claim you are working with 800x600 32bit image data
for (int y = 0; y < 600; ++y)
{
const char* src = &memblock[y * pitch]; // is this really the pitch of the file? you didn't specify....
char* dst = &p[y * pitch];
memcpy(dst, src, 800*4); // width * size of a pixel
}
this code assumes you didn't make a mistake somewhere else such as the size of the texture or the format of the pixels. You will also notice some unknowns in the code you need to find out.
You might also try SDL_UpdateTexture which will accept a pointer to pixels like you are attempting in your code. However, it is likely to be much slower than SDL_LockTexture and you still need to actually read the file (or better yet mmap it) to get the pixels to pass in.
Yet a 3rd option, if SDL_Image knows how to read your "RAW" file, is to use IMG_Load to get an SDL_Surface of your image then creating a texture from that surface using SDL_CreateTextureFromSurface
Related
I'm trying to set the transparency of my image. This code works fine in C++, but when I try the same code in C it no longer works. On executing the code, the image displays, however the black background remains. This is the code I'm using. Can anyone help me identify the issue?
SDL_Texture* Utilities_loadImage( SDL_Renderer* r, const char* file )
{
/* load an image into memory using SDL_image library function */
SDL_Surface* surface = SDL_LoadBMP(file);
if(!surface)
{
printf("error creating surface: %s\n", SDL_GetError());
return NULL;
}
if(SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGB(surface->format, 0, 0, 0)) != 0)
{
printf("Unable to set colourkey: %s", SDL_GetError());
}
/* convert image into a texture for use by the grafx hardware */
SDL_Texture* texture = SDL_CreateTextureFromSurface(r, surface);
/* get rid of surface */
SDL_FreeSurface(surface);
if(!texture)
{
printf("error creating texture: %s\n", SDL_GetError());
return NULL;
}
return texture;
}
Well maybe in this specific line:
SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGB(surface->format, 0, 0, 0))
you are calling the same variable "surface" and maybe the you background it's in other variable for example:
SDL_SetColorKey(background, SDL_TRUE, SDL_MapRGB(surface->format, 0, 0, 0))
and the variable surface it's the principal image that what you try to move or work.
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
typedef struct{
SDL_Texture *texture; // The image/sprite itself
int width; // Sprite width
int height; // Sprite height
} Sprite;
Sprite *createSprite(SDL_Renderer *r, char *path);
int main(int argc, char *argv[]){
// Initialise SDL and SDL_image
SDL_Init(SDL_INIT_VIDEO);
IMG_Init(IMG_INIT_PNG);
// Create window
SDL_Window *window = SDL_CreateWindow(
"VN", // Title
SDL_WINDOWPOS_CENTERED, // Initial x position
SDL_WINDOWPOS_CENTERED, // Initial y position
1280, // Width
720, // Height
0 // Flags
);
if(window == NULL){
printf("Failed to create window. %s\n", SDL_GetError());
return 1;
}
// Create renderer
SDL_Renderer *renderer = SDL_CreateRenderer(
window, // Window
-1, // Monitor index (-1 for first available)
SDL_RENDERER_ACCELERATED // Flags
);
if(renderer == NULL){
printf("Failed to create renderer. %s\n", SDL_GetError());
return 1;
}
// Set up event handling
SDL_Event event;
while(1){
// Handle events/input
while(SDL_PollEvent(&event) != 0){
// Check if user wants to quit (press window close button)
if(event.type == SDL_QUIT){
return 1;
}
}
// Set screen colour to white
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
// Render white to screen (clear screen)
SDL_RenderClear(renderer);
// Update screen
SDL_RenderPresent(renderer);
}
// Exit SDL and SDL_image
SDL_Quit();
IMG_Quit();
return 0;
}
Sprite *createSprite(SDL_Renderer *r, char *path){
printf("Entered createSprite()\n");
// Create a Sprite structure, containing an image (texture) and its dimensions
Sprite *newSprite = NULL;
SDL_Texture *spriteTexture = NULL;
// Temporary surface (for loading texture)
SDL_Surface *tempSurface = NULL;
// Load image from path
tempSurface = IMG_Load(path);
if(tempSurface == NULL){
printf("Failed to load image '%s'. %s\n", path, IMG_GetError());
} else{
spriteTexture = SDL_CreateTextureFromSurface(r, tempSurface);
if(spriteTexture == NULL){
printf("Failed to create texture from '%s'. %s\n", path, SDL_GetError());
} else{
// Store texture, image width & height in Sprite structure
newSprite->texture = spriteTexture;
newSprite->width = tempSurface->w;
newSprite->height = tempSurface->h;
}
}
// Free memory of temp surface
SDL_FreeSurface(tempSurface);
printf("Leaving createSprite()\n");
return newSprite;
}
I'm attempting to load an image using the SDL_image addon, and store its width and height in a structure (along with a texture created from the surface).
It crashes when attempting to run the newSprite->texture = spriteTexture; section on Line 93. That's as much information as I can give I'm afraid. Any ideas?
I'm attempting to load an image using the SDL_image addon, and store its width and height in a structure (along with a texture created from the surface).
The problem is you're not allocating memory for Sprite in your createSprite function. You set it to NULL and than try to access that location which of course fails.
Try this:
Sprite *createSprite(SDL_Renderer *r, char *path){
printf("Entered createSprite()\n");
// Create a Sprite structure, containing an image (texture) and its dimensions
Sprite *newSprite = new Sprite(); //<<- this is where you allocate the memory
SDL_Texture *spriteTexture = NULL;
// Temporary surface (for loading texture)
SDL_Surface *tempSurface = NULL;
// Load image from path
tempSurface = IMG_Load(path);
if(tempSurface == NULL){
printf("Failed to load image '%s'. %s\n", path, IMG_GetError());
} else{
spriteTexture = SDL_CreateTextureFromSurface(r, tempSurface);
if(spriteTexture == NULL){
printf("Failed to create texture from '%s'. %s\n", path, SDL_GetError());
} else{
// Store texture, image width & height in Sprite structure
newSprite->texture = spriteTexture;
newSprite->width = tempSurface->w;
newSprite->height = tempSurface->h;
}
}
// Free memory of temp surface
SDL_FreeSurface(tempSurface);
printf("Leaving createSprite()\n");
return newSprite;
}
I have a structure which contains 16-bit grayscale image from scientific camera, structure is like this:
struct qscam_image {
int row;
int col;
int max_gray; // this should be 65535
unsigned short *data;
};
Then I have program which should display updated grayscale image each second or so (depending on time which takes to transfer data from camera).
#include <GL/gl.h>
#include <GL/glu.h>
#include "qscapi.h"
#include <SDL/SDL.h>
static void render(SDL_Surface * sf)
{
SDL_Surface * screen = SDL_GetVideoSurface();
if(SDL_BlitSurface(sf, NULL, screen, NULL) == 0)
SDL_UpdateRect(screen, 0, 0, 0, 0);
}
int main(int argc, char **argv)
{
//initialize camera
struct qscam_image *image = NULL;
qscam_create_image(&image);
qscam_init();
//initialize SDL
SDL_Event ev;
int active;
/* Initialize SDL */
if(SDL_Init(SDL_INIT_VIDEO) != 0)
fprintf(stderr,"Could not initialize SDL: %s\n",SDL_GetError());
SDL_SetVideoMode(1900, 1080, 24, SDL_HWSURFACE);
/* Main loop */
active = 1;
while(active)
{
/* Handle events */
while(SDL_PollEvent(&ev))
{
if(ev.type == SDL_QUIT)
active = 0; /* End */
}
qscam_get_image(0.030, &image); //get image from camera
/* create the image variable */
Uint32 mask = 0xff00; // gray
SDL_Surface* print_image = SDL_CreateRGBSurfaceFrom
(image->data, image->col, image->row,
16, image->col*2, mask, mask, mask, 0);
render(print_image);
}
/* Exit */
qscam_disconnect();
qscam_destroy_image(&image);
SDL_Quit();
return 0;
}
It works to some extent, with mask=0x00ff it displays random noise but that is only 8-bit image. With mask=0xffff nothing is displayed and with mask=0xff00 again image is displayed, but in a way that leads me to think that the image matrix should be transposed.
First question: What is the proper way to display 16-bit grayscale image (which is in memory saved as unsigned short[]) using SDL and c?
And second question: Is there any easy way to transpose image matrix with SDL or do I have to write separate function?
I just moved from SDL 1.2 to SDL 2.0 and I initially enjoyed similar performance (65+ FPS), then added Vsync in the renderer options and enjoyed a smooth 60 FPS with no tearing, then I added the flag SDL_WINDOW_RESIZABLE and the framerate dropped a lot (40 FPS without Vsync, 30 FPS with) and tearing appeared even with Vsync and side-scrolling became quite jittery. Whether or not I do anything to handle the resize event doesn't change anything. What am I doing wrong?
I'm using SDL 2.0.3 on VS2010 compiling in 64 bits. Here's the relevant code:
// This creates the window and everything associated with it
void graphics_init(srgb_t **sfb)
{
static int init=1;
if (init)
{
init = 0;
SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | SDL_INIT_AUDIO);
}
window = SDL_CreateWindow ( VERSION, // window title
SDL_WINDOWPOS_UNDEFINED, // initial x position
SDL_WINDOWPOS_UNDEFINED, // initial y position
0, // width, in pixels
0, // height, in pixels
SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED); // flags
SDL_GetWindowSize(window, &kW, &kH);
if (window == NULL)
{
fprintf(stderr, "Could not create window: %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, kW, kH);
*sfb = calloc (kW*kH, sizeof(srgb_t));
}
int main()
{
graphics_init(&sfb);
while (exit_flag==0)
{
... // main loop stuff that fills my buffer sfb
// Blits sfb buffer to screen
SDL_UpdateTexture(texture, NULL, sfb, kW * sizeof(srgb_t));
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
I have a function which save a hwnd into a ppm file.
This function is inspired by a msdn example.
Both the msdn sample and my function work but ... I have an issue ...
But first, here is the function.
int CaptureAnImage(HWND hWnd)
{
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
RECT rc;
BITMAPINFOHEADER bi;
DWORD dwBmpSize;
HANDLE hDIB;
char *lpbitmap;
int w, h;
FILE *f;
// Retrieve the handle to a display device context for the client
// area of the window.
hdcWindow = GetDC(hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if(!hdcMemDC) {
MessageBox(hWnd, "CreateCompatibleDC has failed","Failed", MB_OK);
goto done;
}
// Get the client area for size calculation
GetClientRect(hWnd, &rc);
w = rc.right - rc.left;
h=rc.bottom-rc.top;
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, w, h);
if(!hbmScreen) {
MessageBox(hWnd, "CreateCompatibleBitmap Failed","Failed", MB_OK);
goto done;
}
// Select the compatible bitmap into the compatible memory DC.
SelectObject(hdcMemDC,hbmScreen);
// Bit block transfer into our compatible memory DC.
if(!BitBlt(hdcMemDC,
0,0,
w, h,
hdcWindow,
0,0,
SRCCOPY)) {
MessageBox(hWnd, "BitBlt has failed", "Failed", MB_OK);
goto done;
}
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = w;
bi.biHeight = h;
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;
bi.biSizeImage = 0;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = 0;
bi.biClrImportant = 0;
dwBmpSize = w*bi.biBitCount*h;
// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc
// have greater overhead than HeapAlloc.
hDIB = GlobalAlloc(GHND,dwBmpSize);
lpbitmap = (char *)GlobalLock(hDIB);
// Gets the "bits" from the bitmap and copies them into a buffer
// which is pointed to by lpbitmap.
GetDIBits(hdcWindow, hbmScreen, 0,
(UINT)h,
lpbitmap,
(BITMAPINFO *)&bi, DIB_RGB_COLORS);
f = fopen("./test.ppm", "wb");
if (!f) {
fprintf(stderr, "cannot create ppm file\n");
goto done;
}
fprintf(f, "P6\n%d %d\n255\n", w, h);
fwrite((LPSTR)lpbitmap, dwBmpSize, 1, f);
fclose(f);
//Unlock and Free the DIB from the heap
GlobalUnlock(hDIB);
GlobalFree(hDIB);
//Clean up
done:
DeleteObject(hbmScreen);
DeleteObject(hdcMemDC);
ReleaseDC(hWnd,hdcWindow);
return 0;
}
So here is the resulting image:
http://imageshack.us/photo/my-images/853/test2ne.jpg/
As you can see, there is a problem in the width size. Maybe because of the border of the window ?
If in the code, I change "w = rc.right - rc.left;" into "w = rc.right - rc.left - 10;", it's better. But I don't understand why I have to put "-10" and ... some pixel are missing on the right of the picture (maybe 10 pixels ?)
http://imageshack.us/photo/my-images/207/test3jq.jpg
And the last question:
is there any way to ask to GetDIBits function to put my byte in the inverted order ?
I don't wand to do a pixel by pixel copy since it will cost some cpu time. (ok, you may say that since I'm saving this file to disk, then I should not be concerned by cpu time, but my goal is not to save this picture to the disk. I'm doing it for debug purpose only)
thanks in advance for any help
Your problem is that each row of image data in a DIB must be DWORD aligned (i.e. aligned on a multiple of 4 bytes).
dwBmpSize = w*bi.biBitCount*h;
This should actually be:
dwBmpSize = ((w*bi.biBitCount+3)&~3) *h;
You will then have to account for this when writing the PPM file.
Also, the image is upside down because by default DIBs are "bottom-up" (row 0 is at the bottom). To make it "top-down" set the biHeight field to a negative value.