/* ------------------------------------------------------------- * framebuffer_demo.c * ------------------------------------------------------------- * A tiny example that: * * allocates a 600×400 RGB‑888 framebuffer in RAM, * * fills it with a demo pattern, * * opens an SDL2 window, * * streams the framebuffer to the window each frame. * * Build (Linux/macOS): * gcc -Wall -O2 -std=c11 framebuffer_demo.c -lSDL2 -o framebuffer_demo * * Run: * ./framebuffer_demo * * ------------------------------------------------------------- */ #include #include #include #include /* ------------------------------------------------------------- * Configuration * ------------------------------------------------------------- */ #define WIDTH 600 #define HEIGHT 400 /* ------------------------------------------------------------- * Simple pixel format helpers * ------------------------------------------------------------- */ /* The framebuffer will be stored as 32‑bit ARGB (0xAARRGGBB). */ typedef Uint32 pixel_t; /* Build a pixel from separate components (no gamma correction). */ static inline pixel_t make_pixel(Uint8 r, Uint8 g, Uint8 b, Uint8 a) { /* SDL uses little‑endian, so the order in memory is BGRA. * Our 0xAARRGGBB layout works fine with SDL_UpdateTexture. */ return (pixel_t)( (a << 24) | (r << 16) | (g << 8) | b ); } /* ------------------------------------------------------------- * Global framebuffer * ------------------------------------------------------------- */ static pixel_t framebuffer[HEIGHT][WIDTH]; /* ------------------------------------------------------------- * Fill the framebuffer with a demo pattern * ------------------------------------------------------------- */ static void init_framebuffer(void) { for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { /* Example: a smooth red‑to‑green gradient */ Uint8 r = (Uint8)((x * 255) / (WIDTH - 1)); /* left → right */ Uint8 g = (Uint8)((y * 255) / (HEIGHT - 1)); /* top → bottom */ Uint8 b = 0x80; /* constant bluish */ framebuffer[y][x] = make_pixel(r, g, b, 255); } } } /* ------------------------------------------------------------- * Main entry point * ------------------------------------------------------------- */ int main(void) { if (SDL_Init(SDL_INIT_VIDEO) != 0) { fprintf(stderr, "SDL_Init error: %s\n", SDL_GetError()); return EXIT_FAILURE; } /* --------------------------------------------------------- * Create a window and a renderer (software accelerated is fine) * --------------------------------------------------------- */ SDL_Window *window = SDL_CreateWindow( "Framebuffer Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN ); if (!window) { fprintf(stderr, "SDL_CreateWindow error: %s\n", SDL_GetError()); SDL_Quit(); return EXIT_FAILURE; } SDL_Renderer *renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC ); if (!renderer) { fprintf(stderr, "SDL_CreateRenderer error: %s\n", SDL_GetError()); SDL_DestroyWindow(window); SDL_Quit(); return EXIT_FAILURE; } /* --------------------------------------------------------- * Create a texture that will hold the framebuffer. * SDL_PIXELFORMAT_ARGB8888 matches our pixel_t layout. * --------------------------------------------------------- */ SDL_Texture *texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT ); if (!texture) { fprintf(stderr, "SDL_CreateTexture error: %s\n", SDL_GetError()); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return EXIT_FAILURE; } /* --------------------------------------------------------- * Initialise the framebuffer with example data. * --------------------------------------------------------- */ init_framebuffer(); /* --------------------------------------------------------- * Main loop * --------------------------------------------------------- */ bool quit = false; SDL_Event ev; while (!quit) { /* ----- Event handling --------------------------------- */ while (SDL_PollEvent(&ev)) { if (ev.type == SDL_QUIT) { quit = true; } else if (ev.type == SDL_KEYDOWN) { /* Press Escape to quit */ if (ev.key.keysym.sym == SDLK_ESCAPE) quit = true; } } /* ----- Update texture from the framebuffer ------------ */ void *tex_pixels; int tex_pitch; // bytes per row if (SDL_LockTexture(texture, NULL, &tex_pixels, &tex_pitch) != 0) { fprintf(stderr, "SDL_LockTexture error: %s\n", SDL_GetError()); break; } /* Copy line‑by‑line because the pitch may differ from WIDTH*4 */ for (int y = 0; y < HEIGHT; ++y) { memcpy( (Uint8 *)tex_pixels + y * tex_pitch, framebuffer[y], WIDTH * sizeof(pixel_t) ); } SDL_UnlockTexture(texture); /* ----- Render ------------------------------------------ */ SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); /* The loop runs at the monitor refresh rate thanks to VSYNC. */ } /* --------------------------------------------------------- * Cleanup * --------------------------------------------------------- */ SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return EXIT_SUCCESS; }