Atlas - read-pixels.c
Home / ext / SDL / examples / renderer / 17-read-pixels Lines: 1 | Size: 7756 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 * This example creates an SDL window and renderer, and draws a 3 * rotating texture to it, reads back the rendered pixels, converts them to 4 * black and white, and then draws the converted image to a corner of the 5 * screen. 6 * 7 * This isn't necessarily an efficient thing to do--in real life one might 8 * want to do this sort of thing with a render target--but it's just a visual 9 * example of how to use SDL_RenderReadPixels(). 10 * 11 * This code is public domain. Feel free to use it for any purpose! 12 */ 13 14#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ 15#include <SDL3/SDL.h> 16#include <SDL3/SDL_main.h> 17 18/* We will use this renderer to draw into this window every frame. */ 19static SDL_Window *window = NULL; 20static SDL_Renderer *renderer = NULL; 21static SDL_Texture *texture = NULL; 22static int texture_width = 0; 23static int texture_height = 0; 24static SDL_Texture *converted_texture = NULL; 25static int converted_texture_width = 0; 26static int converted_texture_height = 0; 27 28#define WINDOW_WIDTH 640 29#define WINDOW_HEIGHT 480 30 31/* This function runs once at startup. */ 32SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) 33{ 34 SDL_Surface *surface = NULL; 35 char *png_path = NULL; 36 37 SDL_SetAppMetadata("Example Renderer Read Pixels", "1.0", "com.example.renderer-read-pixels"); 38 39 if (!SDL_Init(SDL_INIT_VIDEO)) { 40 SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); 41 return SDL_APP_FAILURE; 42 } 43 44 if (!SDL_CreateWindowAndRenderer("examples/renderer/read-pixels", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_RESIZABLE, &window, &renderer)) { 45 SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); 46 return SDL_APP_FAILURE; 47 } 48 SDL_SetRenderLogicalPresentation(renderer, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX); 49 50 /* Textures are pixel data that we upload to the video hardware for fast drawing. Lots of 2D 51 engines refer to these as "sprites." We'll do a static texture (upload once, draw many 52 times) with data from a bitmap file. */ 53 54 /* SDL_Surface is pixel data the CPU can access. SDL_Texture is pixel data the GPU can access. 55 Load a .png into a surface, move it to a texture from there. */ 56 SDL_asprintf(&png_path, "%ssample.png", SDL_GetBasePath()); /* allocate a string of the full file path */ 57 surface = SDL_LoadPNG(png_path); 58 if (!surface) { 59 SDL_Log("Couldn't load bitmap: %s", SDL_GetError()); 60 return SDL_APP_FAILURE; 61 } 62 63 SDL_free(png_path); /* done with this, the file is loaded. */ 64 65 texture_width = surface->w; 66 texture_height = surface->h; 67 68 texture = SDL_CreateTextureFromSurface(renderer, surface); 69 if (!texture) { 70 SDL_Log("Couldn't create static texture: %s", SDL_GetError()); 71 return SDL_APP_FAILURE; 72 } 73 74 SDL_DestroySurface(surface); /* done with this, the texture has a copy of the pixels now. */ 75 76 return SDL_APP_CONTINUE; /* carry on with the program! */ 77} 78 79/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ 80SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) 81{ 82 if (event->type == SDL_EVENT_QUIT) { 83 return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ 84 } 85 return SDL_APP_CONTINUE; /* carry on with the program! */ 86} 87 88/* This function runs once per frame, and is the heart of the program. */ 89SDL_AppResult SDL_AppIterate(void *appstate) 90{ 91 const Uint64 now = SDL_GetTicks(); 92 SDL_Surface *surface; 93 SDL_FPoint center; 94 SDL_FRect dst_rect; 95 96 /* we'll have a texture rotate around over 2 seconds (2000 milliseconds). 360 degrees in a circle! */ 97 const float rotation = (((float) ((int) (now % 2000))) / 2000.0f) * 360.0f; 98 99 /* as you can see from this, rendering draws over whatever was drawn before it. */ 100 SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); /* black, full alpha */ 101 SDL_RenderClear(renderer); /* start with a blank canvas. */ 102 103 /* Center this one, and draw it with some rotation so it spins! */ 104 dst_rect.x = ((float) (WINDOW_WIDTH - texture_width)) / 2.0f; 105 dst_rect.y = ((float) (WINDOW_HEIGHT - texture_height)) / 2.0f; 106 dst_rect.w = (float) texture_width; 107 dst_rect.h = (float) texture_height; 108 /* rotate it around the center of the texture; you can rotate it from a different point, too! */ 109 center.x = texture_width / 2.0f; 110 center.y = texture_height / 2.0f; 111 SDL_RenderTextureRotated(renderer, texture, NULL, &dst_rect, rotation, ¢er, SDL_FLIP_NONE); 112 113 /* this next whole thing is _super_ expensive. Seriously, don't do this in real life. */ 114 115 /* Download the pixels of what has just been rendered. This has to wait for the GPU to finish rendering it and everything before it, 116 and then make an expensive copy from the GPU to system RAM! */ 117 surface = SDL_RenderReadPixels(renderer, NULL); 118 119 /* This is also expensive, but easier: convert the pixels to a format we want. */ 120 if (surface && (surface->format != SDL_PIXELFORMAT_RGBA8888) && (surface->format != SDL_PIXELFORMAT_BGRA8888)) { 121 SDL_Surface *converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA8888); 122 SDL_DestroySurface(surface); 123 surface = converted; 124 } 125 126 if (surface) { 127 /* Rebuild converted_texture if the dimensions have changed (window resized, etc). */ 128 if ((surface->w != converted_texture_width) || (surface->h != converted_texture_height)) { 129 SDL_DestroyTexture(converted_texture); 130 converted_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, surface->w, surface->h); 131 if (!converted_texture) { 132 SDL_Log("Couldn't (re)create conversion texture: %s", SDL_GetError()); 133 return SDL_APP_FAILURE; 134 } 135 converted_texture_width = surface->w; 136 converted_texture_height = surface->h; 137 } 138 139 /* Turn each pixel into either black or white. This is a lousy technique but it works here. 140 In real life, something like Floyd-Steinberg dithering might work 141 better: https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering*/ 142 int x, y; 143 for (y = 0; y < surface->h; y++) { 144 Uint32 *pixels = (Uint32 *) (((Uint8 *) surface->pixels) + (y * surface->pitch)); 145 for (x = 0; x < surface->w; x++) { 146 Uint8 *p = (Uint8 *) (&pixels[x]); 147 const Uint32 average = (((Uint32) p[1]) + ((Uint32) p[2]) + ((Uint32) p[3])) / 3; 148 if (average == 0) { 149 p[0] = p[3] = 0xFF; p[1] = p[2] = 0; /* make pure black pixels red. */ 150 } else { 151 p[1] = p[2] = p[3] = (average > 50) ? 0xFF : 0x00; /* make everything else either black or white. */ 152 } 153 } 154 } 155 156 /* upload the processed pixels back into a texture. */ 157 SDL_UpdateTexture(converted_texture, NULL, surface->pixels, surface->pitch); 158 SDL_DestroySurface(surface); 159 160 /* draw the texture to the top-left of the screen. */ 161 dst_rect.x = dst_rect.y = 0.0f; 162 dst_rect.w = ((float) WINDOW_WIDTH) / 4.0f; 163 dst_rect.h = ((float) WINDOW_HEIGHT) / 4.0f; 164 SDL_RenderTexture(renderer, converted_texture, NULL, &dst_rect); 165 } 166 167 SDL_RenderPresent(renderer); /* put it all on the screen! */ 168 169 return SDL_APP_CONTINUE; /* carry on with the program! */ 170} 171 172/* This function runs once at shutdown. */ 173void SDL_AppQuit(void *appstate, SDL_AppResult result) 174{ 175 SDL_DestroyTexture(converted_texture); 176 SDL_DestroyTexture(texture); 177 /* SDL will clean up the window/renderer for us. */ 178} 179 180[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.